Встраиваемые функции
Встраиваемые функции
Вызов функции, хоть в си он очень быстрый, отнимает некоторое время. В современном си есть возможность объявлять встраиваемые функции. При компиляции вызов функции будет заменён её телом.
Для объявления встраиваемой функции используется ключевое слово inline (или __inline, __forceinline в зависимости от компилятора)
#include <stdio.h> inline int fun (int a, int b) __attribute__((always_inline)); int main() { int result = fun(2, 3); printf("%d", result); getchar(); return 0; } inline int fun(int a, int b) { return a + b; }
Здесь, для тестирования, использованы атрибуты компилятора gcc, которые форсируют встраивание. Рассмотрим код, который компилируется при использовании inline
0004016f0 <_main>: 4016f0: 55 push %ebp 4016f1: 89 e5 mov %esp,%ebp 4016f3: 83 e4 f0 and $0xfffffff0,%esp 4016f6: 83 ec 20 sub $0x20,%esp 4016f9: e8 c2 00 00 00 call 4017c0 <___main> 4016fe: c7 44 24 18 02 00 00 movl $0x2,0x18(%esp) 401705: 00 401706: c7 44 24 14 03 00 00 movl $0x3,0x14(%esp) 40170d: 00 40170e: 8b 54 24 18 mov 0x18(%esp),%edx 401712: 8b 44 24 14 mov 0x14(%esp),%eax 401716: 01 d0 add %edx,%eax 401718: 89 44 24 1c mov %eax,0x1c(%esp) 40171c: 8b 44 24 1c mov 0x1c(%esp),%eax 401720: 89 44 24 04 mov %eax,0x4(%esp) 401724: c7 04 24 64 50 40 00 movl $0x405064,(%esp) 40172b: e8 a8 1f 00 00 call 4036d8 <_printf> 401730: e8 cb 1f 00 00 call 403700 <_getchar> 401735: b8 00 00 00 00 mov $0x0,%eax 40173a: c9 leave 40173b: c3 retИ без использования (видим вызов функции CALL в строке 10)
004016f0 <_main>: 4016f0: 55 push %ebp 4016f1: 89 e5 mov %esp,%ebp 4016f3: 83 e4 f0 and $0xfffffff0,%esp 4016f6: 83 ec 20 sub $0x20,%esp 4016f9: e8 d2 00 00 00 call 4017d0 <___main> 4016fe: c7 44 24 04 03 00 00 movl $0x3,0x4(%esp) 401705: 00 401706: c7 04 24 02 00 00 00 movl $0x2,(%esp) 40170d: e8 24 00 00 00 call 401736 <_fun> 401712: 89 44 24 1c mov %eax,0x1c(%esp) 401716: 8b 44 24 1c mov 0x1c(%esp),%eax 40171a: 89 44 24 04 mov %eax,0x4(%esp) 40171e: c7 04 24 64 50 40 00 movl $0x405064,(%esp) 401725: e8 be 1f 00 00 call 4036e8 <_printf> 40172a: e8 e1 1f 00 00 call 403710 <_getchar> 40172f: b8 00 00 00 00 mov $0x0,%eax 401734: c9 leave 401735: c3 ret 00401736 <_fun>: 401736: 55 push %ebp 401737: 89 e5 mov %esp,%ebp 401739: 8b 55 08 mov 0x8(%ebp),%edx 40173c: 8b 45 0c mov 0xc(%ebp),%eax 40173f: 01 d0 add %edx,%eax 401741: 5d pop %ebp 401742: c3 ret 401743: 90 nop
Inline функции имеют ряд недостатков. Во-первых, компилятор может отказать во встраивании функции, если это снижает скорость выполнения. Снижение может происходить в том числе и из-за того, что кеш инструкций будет переполняться. Вообще, inline следует скорее рассматривать как подсказку компилятору, а не руководство к действию.
Во-вторых, для встраиваемых систем, в которых разные функции могут располагаться в разных сегментах памяти, это недопустимо, так как вызов может произойти не в том сегменте, в котором ожидалось.
В-третьих, это даёт достаточно малый прирост производительности, но усложняет процесс сборки, оптимизации и увеличивает время компиляции. Во время внешнего связывания (external linkage) также могут возникнуть проблемы, если функция не была объявлена inline во всех компилируемых модулях. Поэтому часто встраиваемые функции объявляют также статическими.
