Указатели функций во встроенных системах, полезны ли они?

В одном из интервью меня спросили, будет ли полезно использование указателей функций (с точки зрения скорости) при написании кода для встраиваемых систем? Я понятия не имел о встроенной системе, поэтому не мог ответить на вопрос. Просто туманный или расплывчатый ответ. Итак, каковы реальные преимущества? Скорость, читабельность, обслуживание, стоимость?


person Community    schedule 29.01.2011    source источник
comment
У интервьюера явно не было большого опыта работы со встроенными системами и/или программированием, чтобы задать этот вопрос. Преимуществами могут быть как скорость, удобочитаемость, так и обслуживание! Хотя нативные указатели на функции имеют довольно ужасный синтаксис, который вы должны убрать, иначе они вообще не будут читаемы.   -  person Lundin    schedule 29.01.2011
comment
На самом деле этот вопрос можно прочитать двояко: большинство людей считают, что проблема заключается в скорости выполнения, но его также можно прочитать буквально, чтобы задаться вопросом о преимуществах скорости написания кода. Это может быть существенным, если вы используете их как средство для устранения общих черт между немного разными случаями или объектами данных. Части встроенной работы должны выполняться быстро; в других случаях, таких как встроенный пользовательский интерфейс, это больше связано с получением богатой функциональности, работающей в одном, часто является коротким циклом разработки.   -  person Chris Stratton    schedule 30.01.2011
comment
Уловка, когда интервьюер задает двусмысленный вопрос, заключается в том, чтобы задать умные встречные вопросы, чтобы уточнить. Это может быть именно тем, что ищет интервьюер.   -  person Clifford    schedule 30.01.2011
comment
Я дал баллы Крису и Клиффорду по этому поводу, реальный ответ - это зависит. Вам нужно выяснить, в чем заключается настоящий вопрос, задавая хорошие вопросы, отвечая на них за и против, рискованно, но быстро, ремонтопригодно, но медленно и т. д. Подробное количество битов и байтов или разница в наносекундах не так интересна, как то, как вы подходить к проблеме в стрессовой ситуации.   -  person old_timer    schedule 31.01.2011


Ответы (10)


Я думаю, что, возможно, ответ Вирена Шакья упускает из виду тот момент, который пытался выявить интервьюер. В некоторых конструкциях использование указателя на функцию может ускорить выполнение. Например, если у вас есть индекс, его использование для индексации массива указателей на функции может быть быстрее, чем большой переключатель.

Однако если вы сравниваете вызов статической функции с вызовом через указатель, то Вирен прав, указывая, что существует дополнительная операция для загрузки переменной указателя. Но никто разумно не пытается использовать указатель на функцию таким образом (просто в качестве альтернативы прямому вызову).

Вызов функции через указатель не является альтернативой прямому вызову. Итак, вопрос о «преимуществе» некорректен; они используются в различных обстоятельствах, часто для упрощения другой логики кода и потока управления, а не просто для того, чтобы избежать вызова статической функции. Их полезность заключается в том, что определение вызываемой функции выполняется динамически во время выполнения вашим кодом, а не статически компоновщиком. В этом смысле они, конечно, полезны во встроенных системах, но не по какой-либо причине, связанной конкретно со встроенными системами.

person Clifford    schedule 29.01.2011
comment
Хороший ответ, учитывающий динамическое (во время выполнения) решение о том, что вызывать с помощью указателя функции. Также полезно отметить, что преимущества/использование указателей на функции действительно не имеют ничего общего со встроенными системами как таковыми. - person Dan; 30.01.2011
comment
Спасибо Клиффорд за разъяснения. - person Viren; 31.01.2011

Есть много применений.

Самое важное использование указателей функций во встроенных системах — это создание векторных таблиц. Многие архитектуры MCU используют таблицу адресов, расположенную в NVM, где каждый адрес указывает на ISR (процедуру обслуживания прерываний). Такая таблица векторов может быть записана на C как массив указателей на функции.

Указатели функций также полезны для функций обратного вызова. В качестве примера из реального мира, на днях я писал драйвер для встроенных часов реального времени. На чипе были только одни часы, а мне нужно было много таймеров. Это было решено путем сохранения счетчика для каждого программного таймера, который был увеличен прерыванием часов реального времени. Тип данных выглядел примерно так:

typedef struct
{
  uint16_t counter;
  void (*callback)(void);

} Timer_t;

Когда аппаратный таймер совпадал с программным таймером, вызывалась функция обратного вызова, указанная пользователем, через указатель функции, хранящийся вместе со счетчиком. Нечто подобное приведенному выше является довольно распространенной конструкцией во встраиваемых системах.

Указатели функций также полезны при создании загрузчиков и т. д., когда вы будете писать код в NVM во время выполнения, а затем вызывать его. Вы можете сделать это с помощью указателя на функцию, но не с помощью связанной функции, так как кода на самом деле нет во время компоновки.

Указатели функций, конечно, как уже упоминалось, полезны для многих оптимизаций, таких как оптимизация оператора switch, где каждый «случай» является соседним числом.

person Lundin    schedule 29.01.2011

Еще одна вещь, которую следует учитывать, это то, что этот вопрос будет хорошей возможностью продемонстрировать, как вы принимаете дизайнерские решения в процессе разработки. Один ответ, который я могу себе представить, будет развернуться и рассмотреть, каковы ваши альтернативы реализации. Взяв страницу из ответов Кейси и Лундина, я обнаружил, что функции обратного вызова очень полезны для изоляции моих модулей друг от друга и упрощения изменений кода, потому что мой код находится на стадии постоянного прототипирования, и все меняется быстро и часто. Что меня сейчас беспокоит, так это простота разработки, а не скорость.

В моем случае мой код обычно включает в себя наличие нескольких модулей, которые должны сигнализировать друг другу, чтобы синхронизировать порядок операций. Ранее я реализовал это как множество флагов и структур данных с внешней связью. С этой реализацией две проблемы обычно поглощали мое время:

  1. Поскольку любой модуль может обращаться к внешним переменным, я много времени трачу на контроль каждого модуля, чтобы убедиться, что эти переменные используются по назначению.
  2. Если другой разработчик вводил новый флаг, я ловил себя на том, что копаюсь в нескольких модулях в поисках оригинального объявления и (надеюсь) описания использования в комментариях.

С функциями обратного вызова эта проблема исчезает, потому что функция становится сигнальным механизмом, и вы пользуетесь следующими преимуществами:

  1. Взаимодействие модулей обеспечивается функциональными интерфейсами, и вы можете проверить предварительные/пост-условия.
  2. Меньшая потребность в глобально общих структурах данных, поскольку обратный вызов служит интерфейсом для внешних модулей.
  3. Уменьшенная связанность означает, что я могу относительно легче менять код.

На данный момент я возьму на себя удар по производительности, поскольку мое устройство по-прежнему работает адекватно даже со всеми дополнительными вызовами функций. Я рассмотрю свои альтернативы, когда эта производительность станет более серьезной проблемой.

Возвращаясь к вопросу с собеседования, даже если вы, возможно, не так хорошо разбираетесь в тонкостях указателей функций, я думаю, вы все равно будете ценным кандидатом, зная, что вы осведомлены о компромиссах, сделанных в процессе проектирования.

person spade78    schedule 06.02.2011

Вы выигрываете в скорости, но теряете в удобочитаемости и обслуживании. Вместо дерева если-то-иначе, если a, то fun_a(), иначе, если b, то fun_b(), иначе если c, то fun_c(), иначе fun_default(), и делать это каждый раз, вместо этого, если a, то fun =fun_a, иначе если b, то fun=fun_b и т. д., и вы делаете это один раз, с этого момента просто вызывайте fun(). Намного быстрее. Как уже отмечалось, вы не можете встроить, что является еще одним приемом скорости, но встраивание в дерево if-then-else не обязательно делает его быстрее, чем без встраивания, и, как правило, не так быстро, как указатель функции.

Вы немного теряете читабельность и обслуживание, потому что вам нужно выяснить, где установлен fun(), как часто он меняется, если вообще когда-либо, убедитесь, что вы не вызываете его до его настройки, но это все еще одно имя для поиска, которое вы можете использовать для поиска и поддерживать все места, где он используется.

По сути, это трюк со скоростью, позволяющий избежать деревьев «если-то-иначе» каждый раз, когда вы хотите выполнить функцию. Если производительность не критична, то fun() может быть статическим и содержать в себе дерево if-then-else.

РЕДАКТИРОВАТЬ Добавление нескольких примеров, чтобы объяснить, о чем я говорил.

extern unsigned int fun1 ( unsigned int a, unsigned int b );

unsigned int (*funptr)(unsigned int, unsigned int);

void have_fun ( unsigned int x, unsigned int y, unsigned int z )
{
    unsigned int j;
    funptr=fun1;

    j=fun1(z,5);
    j=funptr(y,6);
}

Компиляция дает это:

have_fun:
    stmfd   sp!, {r3, r4, r5, lr}
    .save {r3, r4, r5, lr}
    ldr r4, .L2
    mov r5, r1
    mov r0, r2
    mov r1, #5
    ldr r2, .L2+4
    str r2, [r4, #0]
    bl  fun1
    ldr r3, [r4, #0]
    mov r0, r5
    mov r1, #6
    blx r3
    ldmfd   sp!, {r3, r4, r5, pc}

Я предполагаю, что Клиффорд говорил о том, что прямой вызов, если он достаточно близок (в зависимости от архитектуры), является одной инструкцией.

    bl  fun1

Где указатель на функцию будет стоить вам как минимум два

    ldr r3, [r4, #0]
    blx r3

Я также упомянул, что разница между прямым и косвенным является дополнительной нагрузкой, которую вы несете.

Прежде чем двигаться дальше, стоит упомянуть плюсы и минусы инлайнинга. В случае ARM, который используется в этих примерах, соглашение о вызовах использует r0-r3 для входящих параметров функции и r0 для возврата. Таким образом, вход в функцию have_fun() с тремя параметрами означает, что у r0-r3 есть содержимое. В ARM также предполагается, что функция может уничтожить r0-r3, поэтому функция have_fun() должна сохранить входные данные, а затем поместить два входа функции fun1() в r0 и r1, так что происходит небольшой танец регистров.

    mov r5, r1
    mov r0, r2
    mov r1, #5
    ldr r2, .L2+4
    str r2, [r4, #0]
    bl  fun1

Компилятор был достаточно умен, чтобы понять, что нам никогда не нужны были первые входные данные для функции have_fun(), поэтому r0 был отброшен и разрешен для немедленного изменения. Кроме того, компилятор был достаточно умен, чтобы знать, что нам никогда не понадобится третий параметр, z (r2), после отправки его в fun1() при первом вызове, поэтому ему не нужно было сохранять его в верхнем регистре. R1, однако, второй параметр для have_fun() необходимо сохранить, поэтому он помещается в регистратор, который не будет уничтожен fun1().

Вы можете видеть, что то же самое происходит для второго вызова функции.

Предполагая, что fun1() — это простая функция:

inline unsigned int fun1 ( unsigned int a, unsigned int b )
{
    return(a+b);
}

Когда вы встраиваете fun1(), вы получаете что-то вроде этого:

    stmfd   sp!, {r4, lr}
    mov r0, r1
    mov r1, #6
    add r4, r2, #5

Компилятору не нужно перетасовывать нижние регистры для подготовки к вызову. Точно так же вы могли заметить, что r4 и lr сохраняются в стеке, когда мы вводим hello_fun(). При таком соглашении о вызовах ARM функция может уничтожить r0-r3, но должна сохранить все остальные регистры, так как в этом случае функции have_fun() потребовалось более четырех регистров, чтобы выполнить свою задачу, она сохранила содержимое r4 в стеке, чтобы можно было использовать Это. Точно так же эта функция, когда я ее скомпилировал, вызвала другую функцию, инструкция bl/blx использует/уничтожает регистр lr (r14), поэтому для возврата функции have_fun() мы также должны сохранить lr в стеке. Упрощенный пример для fun1() не показал этого, но еще одна экономия, которую вы получаете от встраивания, заключается в том, что при входе вызываемая функция не должна настраивать фрейм стека и сохранять регистры, это действительно так, как если бы вы взяли код из функции и вставил его в вызывающую функцию.

Почему бы вам не инлайнить все время? Ну, во-первых, он может и будет использовать больше регистров, и это может привести к большему использованию стека, а стек работает медленно по сравнению с регистрами. Однако наиболее важным является то, что это увеличивает размер вашего двоичного файла, если бы fun1() была функцией хорошего размера, и вы вызывали ее 20 раз в have_fun(), ваш двоичный файл был бы значительно больше. Для современных компьютеров с гигабайтами оперативной памяти несколько сотен или нескольких десятков тысяч байт не имеют большого значения, но для встроенных систем с ограниченными ресурсами это может сделать вас или сломать. На современном гигагерцовом многоядерном десктопе как часто нужно шейпить инструкцию или все-таки пять? Иногда да, но не всегда для каждой функции. Так что только потому, что вам, вероятно, это сойдет с рук на рабочем столе, вы, вероятно, не должны.

Вернемся к указателям на функции. Итак, смысл, который я пытался подчеркнуть своим ответом, заключается в том, в каких ситуациях вы, вероятно, захотите использовать указатель на функцию, каковы варианты использования и в этих случаях использования, насколько это помогает или вредит?

Типы случаев, о которых я думал, - это плагины, или код, специфичный для вызывающего параметра, или общий код, реагирующий на определенное обнаруженное оборудование. Например, гипотетическая программа tar может захотеть вывести данные на ленточный накопитель, в файловую систему или что-то еще, и вы можете написать код с помощью универсальных функций, вызываемых с помощью указателей функций. При входе в программу параметры командной строки указывают вывод, и в этот момент вы устанавливаете указатели функций на конкретные функции устройства.

if(outdev==OUTDEV_TAPE) data_out=data_out_tape;
else if(outdev==OUTDEV_FILE)
{
    //open the file, etc
    data_out=data_out_file;
}
...

Или, возможно, вы не знаете, работаете ли вы на процессоре с fpu или какой тип fpu у вас есть, но вы знаете, что разделение с плавающей запятой, которое вы хотите сделать, может работать намного быстрее с использованием fpu:

if(fputype==FPU_FPA) fdivide=fdivide_fpa;
else if(fputype==FPU_VFP) fdivide=fdivide_vfp;
else fdivide=fdivide_soft;

И, безусловно, вы можете использовать оператор case вместо дерева if-then-else, плюсы и минусы для каждого, некоторые компиляторы в любом случае превращают оператор case в дерево if-then-else, так что это не всегда имеет значение. Я пытался подчеркнуть, что если вы сделаете это один раз:

if(fputype==FPU_FPA) fdivide=fdivide_fpa;
else if(fputype==FPU_VFP) fdivide=fdivide_vfp;
else fdivide=fdivide_soft;

И делайте это везде в программе:

a=fdivide(b,c);

По сравнению с альтернативой без указателя функции, где вы делаете это везде, где хотите разделить:

if(fputype==FPU_FPA) a=fdivide_fpa(b,c);
else if(fputype==FPU_VFP) a=fdivide_vfp(b,c);
else a=fdivide_soft(b,c);

Подход с указателем на функцию, хотя и стоит вам дополнительного ldr при каждом вызове, намного дешевле, чем множество инструкций, необходимых для дерева if-then-else. Вы платите немного вперед, чтобы установить указатель fdivide один раз, а затем платите дополнительный ldr за каждый экземпляр, но в целом это быстрее, чем это:


unsigned int fun1 ( unsigned int a, unsigned int b );
unsigned int fun2 ( unsigned int a, unsigned int b );
unsigned int fun3 ( unsigned int a, unsigned int b );

unsigned int (*funptr)(unsigned int, unsigned int);

unsigned int have_fun ( unsigned int x, unsigned int y, unsigned int z )
{
    unsigned int j;

    switch(x)
    {
        default:
        case 1: j=fun1(y,z); break;
        case 2: j=fun2(y,z); break;
        case 3: j=fun3(y,z); break;
    }
    return(j);
}

unsigned int more_fun ( unsigned int x, unsigned int y, unsigned int z )
{
    unsigned int j;
    j=funptr(y,z);
    return(j);
}

дает нам это:

    cmp r0, #2
    beq .L3
    cmp r0, #3
    beq .L4
    mov r0, r1
    mov r1, r2
    b   fun1
.L3:
    mov r0, r1
    mov r1, r2
    b   fun2
.L4:
    mov r0, r1
    mov r1, r2
    b   fun3

вместо этого

    mov r0, r1
    ldr r3, .L7
    mov r1, r2
    blx r3

В случае по умолчанию дерево if-then-else записывает два сравнения и два beq перед прямым вызовом функции. В основном иногда дерево if-then-else будет быстрее, а иногда указатель на функцию быстрее.

Еще один комментарий, который я сделал, заключается в том, что если вы используете встраивание, чтобы сделать дерево if-then-else быстрее, вместо указателя на функцию, встраивание всегда будет быстрее, верно?

unsigned int fun1 ( unsigned int a, unsigned int b )
{
    return(a+b);
}
unsigned int fun2 ( unsigned int a, unsigned int b )
{
    return(a-b);
}
unsigned int fun3 ( unsigned int a, unsigned int b )
{
    return(a&b);
}

unsigned int have_fun ( unsigned int x, unsigned int y, unsigned int z )
{
    unsigned int j;

    switch(x)
    {
        default:
        case 1: j=fun1(y,z); break;
        case 2: j=fun2(y,z); break;
        case 3: j=fun3(y,z); break;
    }
    return(j);
}

дает

have_fun:
    cmp r0, #2
    rsbeq   r0, r2, r1
    bxeq    lr
    cmp r0, #3
    addne   r0, r2, r1
    andeq   r0, r2, r1
    bx  lr

LOL, ARM помогла мне в этом. Это хорошо. Вы можете себе представить, что для универсального процессора вы получите что-то вроде

    cmp r0, #2
    beq .L3
    cmp r0, #3
    beq .L4
    and r0,r1,r2
    bx lr
.L3:
    sub r0,r1,r2
    bx lr
.L4:
    add r0,r1,r2
    bx lr

Вы по-прежнему записываете сравнения, чем больше у вас случаев, тем длиннее дерево «если-то-иначе». Для среднего случая не требуется много больше времени, чем решение указателя функции.

    mov r0, r1
    ldr r1, .L7
    ldr r3,[r1]
    mov r1, r2
    blx r3

Затем я также упомянул удобочитаемость и обслуживание, используя подход с указателем на функцию, вам всегда нужно знать, был ли назначен указатель на функцию, прежде чем использовать его. Вы не всегда можете просто найти это имя функции и найти то, что ищете, в чужом коде, в идеале вы найдете одно место, где назначен этот указатель, а затем вы можете найти настоящие имена функций.

Да, есть много других вариантов использования указателей на функции, и те, которые я описал, могут быть решены многими другими способами, эффективными или нет. Я пытался дать плакату несколько идей о том, как продумывать различные сценарии.

Я думаю, что самый важный ответ на этот вопрос интервью не в том, что есть правильный или неправильный ответ, потому что я думаю, что нет. Но чтобы увидеть, что интервьюируемый знает о том, что компиляторы делают или не делают, о том, что я описал выше. Вопрос интервью для меня — это несколько вопросов, понимаете ли вы, что на самом деле делает компилятор, какие инструкции он генерирует. Вы понимаете, что меньше или больше инструкций не обязательно быстрее. понимаете ли вы эти различия между разными процессорами или хотя бы имеете практические знания хотя бы для одного процессора. Затем он переходит к удобочитаемости и обслуживанию. Это еще один поток вопросов, который связан с вашим опытом чтения кода других людей, а затем с поддержкой вашего собственного кода или кода других людей. На мой взгляд, это остроумный вопрос.

person old_timer    schedule 29.01.2011
comment
и это не имеет ничего общего со встроенным или не встроенным. - person old_timer; 29.01.2011
comment
Я сомневаюсь, что предлагаемое вами использование вообще быстрее, не говоря уже о том, что намного быстрее. В первом вы вызываете функцию, во втором вы устанавливаете переменную, а затем вызываете функцию; как так быстрее, а дерево если-то-иначе не искореняется вообще. Я бы предложил очень немного медленнее. Это может незначительно уменьшить размер кода. - person Clifford; 29.01.2011
comment
@Clifford на руке, например, bl r0 - довольно быстрое решение во время выполнения, по сравнению со случайным количеством кода, затем адресом bl, иначе случайным количеством кода, затем адресом bl, иначе случайным количеством кода ... Я говорил о практическом использовании. при прямом сравнении встроенного и непосредственного использования функции с указателем на эту функцию (а не использованием), тогда скорость указана в следующем порядке: встроенный (выполнение функции), прямой (сохранение и подготовка регистров, адрес bl, стек, функция, стек). , bx lr), косвенные (сохранение и подготовка регистров, ldr rx, адрес для функции, bl rx, содержимое стека, функция, содержимое стека, bx lr) - person old_timer; 29.01.2011
comment
При правильном использовании вы можете повысить удобочитаемость и организацию, используя указатели на функции, чтобы заменить логику выбора хорошо продуманной абстракцией. - person Chris Stratton; 30.01.2011
comment
@dwelch: меня не убеждают ваши объяснения, возможно, если вы опубликуете реальные примеры кода, чтобы объяснить свое значение, а не вставлять свой код в текст. Откуда в этом объяснении вдруг появляются случайные куски кода? - person Clifford; 30.01.2011
comment
@Clifford - я считаю, что упомянутые «случайные количества кода» - это условные тесты, которых можно было бы избежать, если бы указатель на соответствующую функцию можно было предварительно предоставить недорого. - person Chris Stratton; 31.01.2011
comment
@ Клиффорд, извини, что ты не понимаешь, о чем я говорю. Вы получили лучший ответ, человек, спрашивавший, был удовлетворен вашим ответом. Не имеет особого смысла, чтобы три профессиональных встроенных инженера обсуждали это на глазах у всех, мы знаем свое дело. Если вам от этого станет лучше, я удалю свой ответ и откажусь от этого вопроса. - person old_timer; 31.01.2011
comment
@dwelch: Мои извинения, я полагаю, что вы, вероятно, правы, но я не уверен, что вы ясно выразили это - это может быть только я. Я чувствовал, что было бы понятнее, если бы вы разделили псевдокод на блоки разметки кода, а не хоронили его в тексте. Побочным эффектом является то, что я могу отозвать, возможно, поспешное голосование против, если вы отредактируете ответ. @Chris Stratton: Согласен, но ответ, похоже, дает один и тот же условный код в обоих случаях и просто перемещает местоположение вызова функции, которое является источником моего комментария. - person Clifford; 31.01.2011
comment
@Clifford и @Chris Stratton: я добавил к своему ответу намного больше слов, написанных для аудитории, которая не знает, о чем мы говорим, и не нацелены на ответ непосредственно на вашем уровне знаний, но, надеюсь, это объясняет, с чем был мой мыслительный процесс. мои ответы и комментарии. - person old_timer; 01.02.2011
comment
@Dwelch: Это научит меня больше не оспаривать короткий ответ! ;) - person Clifford; 01.02.2011
comment
Пожалуйста, не удаляйте действительные ответы. Даже если они не предназначены для работы, их прочтут другие :) (Возможно, это можно немного отредактировать.) - person XTL; 17.01.2014

Я бы сказал, что они выгодны (с точки зрения скорости) в любой среде, а не только во встроенной. Идея состоит в том, что после того, как указатель был направлен на правильную функцию, для вызова этой функции не требуется дальнейшей логики принятия решения.

person trojanfoe    schedule 29.01.2011
comment
Это все еще может быть не совсем ясно, но я думаю, что вы имеете в виду, что использование указателей на функции может в некоторых обстоятельствах упростить или удалить логику выбора. Скорость достигается не простой заменой статического вызова вызовом указателя; это непрактично и, как указал @Viren, не быстрее. - person Clifford; 29.01.2011

Да, они полезны. Я не уверен, что интервьюер имел в виду. По сути, не имеет значения, встроена система или нет. Если только у вас не сильно ограниченный стек.

  • Скорость Нет, самой быстрой системой будет одна функция, использующая только глобальные переменные и разбросанные по ней команды goto. Удачи с этим.
  • Читаемость Да, это может сбить с толку некоторых людей, но в целом определенный код более удобочитаем с указателями на функции. Это также позволит вам увеличить разделение проблем между различными аспектами исходного кода.
  • Удобство сопровождения Да, с указателями на функции у вас будет меньше условных выражений, меньше дублированного кода, большее разделение кода и, как правило, более ортогональное программное обеспечение.
person cmcginty    schedule 29.01.2011

Одна из отрицательных сторон указателей на функции заключается в том, что они никогда не будут встроены в места вызова. Это может быть или не быть полезным, в зависимости от того, компилируете ли вы скорость или размер. Если последнее, то они не должны отличаться от обычных вызовов функций.

person leppie    schedule 29.01.2011

Еще один недостаток указателей на функции (по отношению к виртуальным функциям, поскольку они не что иное, как указатели на функции на уровне ядра):

создание функции встроенной && виртуальной заставит компилятор создать внестрочную копию той же функции. Это увеличит размер конечного бинарного файла (при условии его интенсивного использования).

Эмпирическое правило: 1. Не совершать виртуальные вызовы в режиме реального времени.

person Viren    schedule 29.01.2011

Это был вопрос с подвохом. Есть отрасли, где указатели запрещены.

person avra    schedule 07.02.2011

Давайте посмотрим...

Скорость (скажем, мы на ARM): тогда (теоретически):

(Размер инструкции ARM при обычном вызове функции) ‹ (Размер инструкции (инструкций) установки вызова указателя функции)

Поскольку это дополнительный уровень косвенности для настройки вызова указателя функции, он потребует дополнительной инструкции ARM.

PS: Обычный вызов функции: вызов функции, настроенный с помощью BL.

PSS: не знаю их реальных размеров, но это должно быть легко проверить.

person Viren    schedule 29.01.2011
comment
Сомневаюсь, что интервьюер искал этот ответ. - person Clifford; 29.01.2011
comment
Почему вы продолжаете говорить об ARM здесь? Что в исходном вопросе побуждает вас адаптировать свой ответ к архитектуре ARM? - person Dan; 30.01.2011
comment
Из встраиваемых чипов, которые хорошо работают на C, в настоящее время популярны варианты ARM... - person Chris Stratton; 30.01.2011
comment
Извините, я хотел использовать ARM только в качестве примера, а также потому, что он активно используется во многих встраиваемых системах. - person Viren; 31.01.2011