С моей точки зрения, в зависимости от того, есть ли у вас компилятор, способный сделать расширение функции inline
, самый быстрый способ сделать это просто:
inline int add(int a, int b)
{
return a + b;
}
потому что компилятор, вероятно, вообще избегает вызова / возврата подпрограммы и будет использовать лучшее расширение, доступное в каждом случае использования (в большинстве случаев это будет встроено как одна add r3, r8
инструкция). Это может занять во многих современных многоядерных и конвейерных процессорах менее одного такта.
Если у вас нет доступа к такому компилятору, то, вероятно, лучший способ имитировать этот сценарий:
#define add(a,b) ((a) + (b))
и вы будете вставлять, сохраняя при этом функцию обозначения. Но с этим ответом произойдет ошибка помещения, так как вы запросили функцию, в зависимости от приоритета помещения:)
Когда вы думаете о лучшем способе выполнения вызова функции, сначала подумайте, что для небольших функций самая тяжелая задача, которую вы выполняете, - это вообще выполнить вызов подпрограммы ... так как для возврата адреса возврата требуется время, Подумайте, что в этом случае худшая часть - это вызов функции, просто добавление двух ее параметров (для добавления двух значений, хранящихся в регистрах, требуется только одна инструкция, но при вызове функции требуется как минимум три - call, add и return back) Добавление не требует много времени, если добавление уже находится в регистрах, но вызов подпрограммы обычно требует поместить регистр в стек и выгрузить его позже. Это два обращения к памяти, которые будут стоить дороже, даже если они кэшируются в кеше команд.
Конечно, если компилятор знает , что функция кэшируется, и вы используете ее несколько раз с одними и теми же параметрами в одном выражении в одном и том же блоке, он может кэшировать значение результата, которое будет использоваться позже, и сохранить стоимость внесения суммы снова. Все становится так, как будто лучшим способом продолжения является выяснение, с каким именно сценарием мы имеем дело. Но в этот момент основная стоимость добавления двух чисел - это стоимость заключения в конверт функции.
EDIT
Я попробовал следующий пример и скомпилировал его в архитектуре arm7 (raspberry pi B +, компилятор freebsd, clang), и результат был далеко не хорошим:)
inline.c
inline int sum(int a, int b)
{
return a + b;
}
int main()
{
int a = 3, b = 2, c = sum(a, b);
}
в результате:
/* ... */
.file "inline.c"
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
mov r0, #0
bx lr
.Lfunc_end0:
.size main, .Lfunc_end0-main
Как видите, единственный код для main состоял в сохранении 0
возвращаемого значения в r0
(код выхода);)
На всякий случай я скомпилировал add
как функцию внешней библиотеки:
int add(int a, int b);
int main()
{
int a = 3, b = 2, c = sum(a, b);
}
приведет к:
.file "inline.c"
.globl main @ -- Begin function main
.p2align 2
.type main,%function
.code 32 @ @main
main:
.fnstart
@ %bb.0:
.save {r11, lr}
push {r11, lr}
.setfp r11, sp
mov r11, sp
mov r0, #3
mov r1, #2
bl sum <--- the call to the function.
mov r0, #0
pop {r11, pc}
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cantunwind
.fnend
Вы можете видеть, что вызов функции будет выполнен, так или иначе, так как компилятор не был проинформирован о функции, которую он имеет под рукой (даже если код результата не будет использоваться, как функция может иметь побочные эффекты), и в любом случае должен быть вызван.
Кстати, и, как уже упоминалось, способ передачи ссылок на функцию включает в себя разыменование этих указателей, и это обычно означает доступ к памяти (что гораздо дороже, чем сложение двух регистров вместе)