Хотя я могу интуитивно получить большую часть результатов, мне трудно понять, что выводит команду perf report
, особенно для графа вызовов, поэтому я написал глупый тест для решения этой проблемы один раз для всех.
Глупый тест
Я скомпилировал следующее:
gcc -Wall -pedantic -lm perf-test.c -o perf-test
Нет агрессивных оптимизаций, чтобы избежать inlining и т.д.
#include <math.h>
#define N 10000000UL
#define USELESSNESS(n) \
do { \
unsigned long i; \
double x = 42; \
for (i = 0; i < (n); i++) x = sin(x); \
} while (0)
void baz()
{
USELESSNESS(N);
}
void bar()
{
USELESSNESS(2 * N);
baz();
}
void foo()
{
USELESSNESS(3 * N);
bar();
baz();
}
int main()
{
foo();
return 0;
}
Плоское профилирование
perf record ./perf-test
perf report
С этими словами я получаю:
94,44% perf-test libm-2.19.so [.] __sin_sse2
2,09% perf-test perf-test [.] [email protected]
1,24% perf-test perf-test [.] foo
0,85% perf-test perf-test [.] baz
0,83% perf-test perf-test [.] bar
Что звучит разумно, так как тяжелая работа на самом деле выполняется __sin_sse2
и [email protected]
, вероятно, всего лишь оболочка, в то время как накладные расходы моих функций учитывают только цикл, в целом: 3*N
итерации для foo
, 2*N
для двух других.
Иерархическое профилирование
perf record -g ./perf-test
perf report -G
perf report
Теперь верхние столбцы, которые я получаю, равны двум: Children
(результат сортируется по умолчанию) и Self
(те же самые служебные данные плоского профиля).
Вот где я начинаю чувствовать, что я что-то пропустил: независимо от того, что я использую -G
или нет, я не могу объяснить иерархию в терминах "x calls y" или "y вызывается x", например:
-
без
-G
( "y вызывается x" ):- 94,34% 94,06% perf-test libm-2.19.so [.] __sin_sse2 - __sin_sse2 + 43,67% foo + 41,45% main + 14,88% bar - 37,73% 0,00% perf-test perf-test [.] main main __libc_start_main - 23,41% 1,35% perf-test perf-test [.] foo foo main __libc_start_main - 6,43% 0,83% perf-test perf-test [.] bar bar foo main __libc_start_main - 0,98% 0,98% perf-test perf-test [.] baz - baz + 54,71% foo + 45,29% bar
- Почему
__sin_sse2
вызываетсяmain
(косвенно?),foo
иbar
, но неbaz
? - Почему функции иногда содержат процент и иерархию (например, последний экземпляр
baz
), а иногда и нет (например, последний экземплярbar
)?
- Почему
-
с
-G
( "x вызывает y" ):- 94,34% 94,06% perf-test libm-2.19.so [.] __sin_sse2 + __sin_sse2 + __libc_start_main + main - 37,73% 0,00% perf-test perf-test [.] main - main + 62,05% foo + 35,73% __sin_sse2 2,23% [email protected] - 23,41% 1,35% perf-test perf-test [.] foo - foo + 64,40% __sin_sse2 + 29,18% bar + 3,98% [email protected] 2,44% baz __libc_start_main main foo
- Как интерпретировать первые три записи в
__sin_sse2
? -
main
вызываетfoo
и это нормально, но почему, если он вызывает__sin_sse2
и[email protected]
(косвенно?), он также не вызываетbar
иbaz
? - Почему
__libc_start_main
иmain
отображаются подfoo
? И почемуfoo
появляется дважды?
- Как интерпретировать первые три записи в
Подозреваю, что существует два уровня этой иерархии, в которых вторая фактически представляет "x-вызовы y" / "y, которые вызывается семантикой x", но я устал догадываться, поэтому я прошу здесь. И документация, похоже, не помогает.
Извините за длинный пост, но я надеюсь, что весь этот контекст может помочь или действовать как ссылка для кого-то еще.