【发布时间】:2015-01-02 12:45:19
【问题描述】:
虽然我可以直观地得到大部分结果,但我很难完全理解 perf report 命令的输出,特别是关于调用图的内容,所以我写了一个愚蠢的测试来解决我的这个问题一次所有人。
愚蠢的测试
我编译了以下内容:
gcc -Wall -pedantic -lm perf-test.c -o perf-test
没有积极的优化来避免内联等。
#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 [.] sin@plt
1,24% perf-test perf-test [.] foo
0,85% perf-test perf-test [.] baz
0,83% perf-test perf-test [.] bar
这听起来很合理,因为繁重的工作实际上是由 __sin_sse2 执行的,sin@plt 可能只是一个包装器,而我的函数的开销只考虑了循环,总体而言:3*N 迭代 foo , 2*N 其他两个。
分层分析
perf record -g ./perf-test
perf report -G
perf report
现在我得到的开销列是两个:Children(输出默认按这一列排序)和Self(与平面配置文件的开销相同)。
这是我开始觉得我错过了什么的地方:无论我是否使用-G,我都无法用“x 调用 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% sin@plt - 23,41% 1,35% perf-test perf-test [.] foo - foo + 64,40% __sin_sse2 + 29,18% bar + 3,98% sin@plt 2,44% baz __libc_start_main main foo- 我应该如何解释
__sin_sse2下的前三个条目? -
main调用foo没关系,但为什么如果它调用__sin_sse2和sin@plt(间接?)它不会调用bar和baz? - 为什么
__libc_start_main和main出现在foo下?为什么foo会出现两次?
- 我应该如何解释
怀疑这个层次结构有两个级别,其中第二个实际上表示“x 调用 y”/“y 由 x 调用”语义,但我猜累了所以我在这里问。而且文档似乎没有帮助。
很抱歉,这篇文章很长,但我希望所有这些上下文都可以对其他人有所帮助或作为参考。
【问题讨论】:
-
我不是
perf的专家,但我知道默认情况下它每秒查看堆栈约 1000 次以收集其数据。因此,您尝试的细粒度分析可能会失败。所以例如当sin_sse2被baz调用时,可能没有任何样本发生。考虑使用gprof,它在存根中编译以捕获每个调用并返回(尽管它有其他问题)。 -
是的,我知道,但它很快,它让我可以记录各种疯狂的事件,例如缓存未命中和每个符号的分支预测错误,而 AFAIK
gprof无法做到。我使用N相当大,正是为了避免你提到的;无论如何,我尝试进一步增加它没有运气,我不知道,但我认为即使在 100M 迭代循环中也不太可能没有收集到样本。