Вопрос в следующем.У меня есть функции (точнее, методы, но все происходит в пределах одного класса, причем экземпляр класса один), которые много раз вызываются в цикле. Представляют собой несложные математические вычисления, этакий синтаксический сахар.
Такие функции, да еще и вызываемые в циклах, имеет смысл объявлять инлайновыми. Это необходимо, чтобы не тратилось время на работу со стеком/регистрами при подготовке вызова, на сам вызов функции, на возврат результата.
То есть предполагается, что в машинном коде вызовов этих функций не должно быть, ибо они непосредственно встаиваются в машинный код вместо своего CALL.
При профилировании через gprof я вижу, что эти inline функции показываются в gprof, и gprof показывает число их вызовов.
Вопрос:
Это gprof такой умный, видит работу инлайновых функций, подсчитывает число их "виртуальных" вызов? Или данные функции, несмотря на директиву inline, скомпилены таки в виде обычных функций, и поэтому их видит gprof?
Пример функций:
inline unsigned char RC5Simple::RC5_GetByteFromWord(RC5_TWORD w, int n)
{
unsigned char b=0;switch (n) {
case 0:
b=(w & 0x000000FF);
break;
case 1:
b=(w & 0x0000FF00) >> 8;
break;
case 2:
b=(w & 0x00FF0000) >> 16;
break;
case 3:
b=(w & 0xFF000000) >> 24;
}// RC5_LOG(( "GetByteFromWord(%.8X, %d)=%.2X\n", w, n, b ));
return b;
}
inline RC5_TWORD RC5Simple::RC5_GetWordFromByte(unsigned char b0,
unsigned char b1,
unsigned char b2,
unsigned char b3)
{
return b0+
(b1 << 8)+
(b2 << 16)+
(b3 << 24);
}А вот как они выглядят в профилировщике:
0.00 0.73 0.00 4289320 0.00 0.00 RC5Simple::RC5_GetByteFromWord(unsigned long, int)
0.00 0.73 0.00 1072330 0.00 0.00 RC5Simple::RC5_GetWordFromByte(unsigned char, unsigned char, unsigned char, unsigned char)
...
0.00 0.00 4289320/4289320 RC5Simple::RC5_GetByteFromWord(unsigned long, int) [423]
0.00 0.00 1072330/1072330 RC5Simple::RC5_GetWordFromByte(unsigned char, unsigned char, unsigned char, unsigned char) [424]
Не знаю как в C++, но в чистом C "inline" -- это лишь рекомендация компилятору встраивать эти функции. Компилятор часто игнорирует эту рекомендацию (когда функция больше 1-2 строк). Единственный способ реально инлайнить функции, который я знаю, -- это макросы (#define). Даже attribute((always_inline)) на помогает, он просто говорит сохранять смысл inline (рекомендация на встраивание) при отключенных оптимизациях (-O0).Смотрим http://en.wikipedia.org/wiki/Inline_function :
The compiler may ignore the programmer’s attempt to inline a function, mainly if it is particularly large.Например вот код:
#include <stdio.h>
#include <stdlib.h>inline int f1(int a)
{
return a + 1;
}inline int f2(int a)
{
int i;for (i = 0; i < 10; ++i) {
if (a < 5)
a += 2;
else
a += 3;
}printf("f2 done\n");
return a;
}int main(void)
{
int x = 5;x = f1(x);
x = f2(x);printf("x = %d\n", x);
return EXIT_SUCCESS;
}Компилируем и смотрим какие функции заинлайнились, а какие нет:
$ gcc -Wall -O2 main.c
$ objdump -DCS a.out | lessВидем следующую картину:
0000000000400450 <main>:
400454: bf 06 00 00 00 mov $0x6,Мi
400459: e8 22 01 00 00 callq 400580 <f2>
...Т.е. компилятор вообще соптимизировал вызов f1(), на этапе компиляции выполнив "5+1=6", а вот f2() уже не инлайнил и она действительно вызывается.
Вывод: проверяйте через objdump, какие функции заинлайнились, а какие нет. Соответственно те, которые не заинлайнились, gprof может видеть.
Вывод 2: если реально нужно инлайнить, попробуйте always_inline, а есть не поможет, то используйте макросы через #define
>[оверквотинг удален]
#include <stdio.h>
#include <stdlib.h>static inline int f1(int a) __attribute__((always_inline));
static inline int f2(int a) __attribute__((always_inline));static inline int f1(int a)
{
return a + 1;
}static inline int f2(int a)
{
int i;for (i = 0; i < 10; ++i) {
if (a < 5)
a += 2;
else
a += 3;
}printf("f2 done\n");
return a;
}int main(void)
{
int x = 5;x = f1(x);
x = f2(x);printf("x = %d\n", x);
return EXIT_SUCCESS;
}
> Компилируем и смотрим какие функции заинлайнились, а какие нет:
>
> $ gcc -Wall -O2 main.c
> $ objdump -DCS a.out | less
>
> Видем следующую картину:
0000000000400546 <main>:
400546: 55 push %rbp
400547: 48 89 e5 mov %rsp,%rbp
40054a: 48 83 ec 10 sub $0x10,%rsp
40054e: c7 45 fc 05 00 00 00 movl $0x5,-0x4(%rbp)
400555: 8b 45 fc mov -0x4(%rbp),%eax
400558: 89 45 f8 mov %eax,-0x8(%rbp)
40055b: 8b 45 f8 mov -0x8(%rbp),%eax
40055e: 83 c0 01 add $0x1,%eax
400561: 89 45 fc mov %eax,-0x4(%rbp)
400564: 8b 45 fc mov -0x4(%rbp),%eax
400567: 89 45 f4 mov %eax,-0xc(%rbp)
40056a: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%rbp)
400571: eb 14 jmp 400587 <main+0x41>
400573: 83 7d f4 04 cmpl $0x4,-0xc(%rbp)
400577: 7f 06 jg 40057f <main+0x39>
400579: 83 45 f4 02 addl $0x2,-0xc(%rbp)
40057d: eb 04 jmp 400583 <main+0x3d>
40057f: 83 45 f4 03 addl $0x3,-0xc(%rbp)
400583: 83 45 f0 01 addl $0x1,-0x10(%rbp)
400587: 83 7d f0 09 cmpl $0x9,-0x10(%rbp)
40058b: 7e e6 jle 400573 <main+0x2d>
40058d: bf 44 06 40 00 mov $0x400644,%edi
400592: e8 79 fe ff ff callq 400410 <puts@plt>
400597: 8b 45 f4 mov -0xc(%rbp),%eax
40059a: 89 45 fc mov %eax,-0x4(%rbp)
40059d: 8b 45 fc mov -0x4(%rbp),%eax
4005a0: 89 c6 mov %eax,%esi
4005a2: bf 4c 06 40 00 mov $0x40064c,%edi
4005a7: b8 00 00 00 00 mov $0x0,%eax
4005ac: e8 6f fe ff ff callq 400420 <printf@plt>
4005b1: b8 00 00 00 00 mov $0x0,%eax
4005b6: c9 leaveq
4005b7: c3 retq
4005b8: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
4005bf: 00
> static inline int f1(int a) __attribute__((always_inline));
> static inline int f2(int a) __attribute__((always_inline));Я вам даже больше скажу, если удалить эти строки, функции всё равно заинлайнятся. Они у вас инлайнятся, потому что они у вас static, а не always_inline. always_inline только и позволяет что инлайнить с отключенными оптимизациями. Если хотите инлайнить всегда -- смотрите в сторону атрибута flatten. И то может не заинлайниться. Поэтому #define рулит :)
Отсюда https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attrib... :
always_inline
Generally, functions are not inlined unless optimization is specified. For functions declared inline, this attribute inlines the function even if no optimization level was specified.flatten
Generally, inlining into a function is limited. For a function marked with this attribute, every call inside this function will be inlined, if possible. Whether the function itself is considered for inlining depends on its size and the current inlining parameters.
А еще хотелось бы покритиковать вашу функцию :) Которая RC5_GetByteFromWord().
1. Функция легко упрощается до формы:
inline unsigned char RC5Simple::RC5_GetByteFromWord(RC5_TWORD w, int n)
{
switch (n) {
case 0:
return w & 0x000000FF;
case 1:
return (w & 0x0000FF00) >> 8;
case 2:
return (w & 0x00FF0000) >> 16;
case 3:
return (w & 0xFF000000) >> 24;
default:
return 0;
}
2. И вообще, то что вы хотите сделать, делается в одну строку :)
inline unsigned char RC5Simple::RC5_GetByteFromWord(RC5_TWORD w, int n)
{
return (w >> (n*8)) & 0xff;
}Тогда может и заинлайнится функция, а если нет, то:
#define GET_BYTE(w, n) ((w) >> ((n)*8) & 0xff)Только у макроса свои недостатки, на которые можно напороться.
>[оверквотинг удален]
> }
>
Ага, вместо того чтобы маскировать с разными масками и сдвигать в краю слова, можно сдвинуть к краю слова и отмаскировать фиксированной маской. Это я не додумал. Так реально без всяких переходов.
Но попробую макросами, чтоб наверняка.
> Это я не додумал.Через пару лет поймешь, что написал в функции, а другие? :)
> Такие функции, да еще и вызываемые в циклах, имеет смысл объявлять инлайновыми.
> Это необходимо, чтобы не тратилось время на работу со стеком/регистрами при
> подготовке вызова, на сам вызов функции, на возврат результата.Функции инлайнить нужно только те, в которых не вызываются другие функции.