C 了解Linux性能报告输出

C 了解Linux性能报告输出,c,linux,perf,C,Linux,Perf,虽然我可以直观地得到大部分结果,但我很难完全理解perf report命令的输出,尤其是与调用图有关的内容,因此我编写了一个愚蠢的测试来彻底解决我的这个问题 愚蠢的测试 我编写了以下内容: gcc -Wall -pedantic -lm perf-test.c -o perf-test 没有积极的优化,以避免内联等 #include <math.h> #define N 10000000UL #define USELESSNESS(n)

虽然我可以直观地得到大部分结果,但我很难完全理解
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;
}
有了这些,我得到了:

  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@plt
可能只是一个包装器,而我的函数的开销只考虑了循环,总的来说:
foo
3*N
迭代,其他两个的
2*N

层次分析 现在,我得到的开销列有两个:
Children
(默认情况下,输出按此列排序)和
Self
(与平面配置文件的开销相同)

在这里,我开始觉得我错过了一些东西:不管我是否使用了
-G
,我都无法解释“x调用y”或“y被x调用”的层次结构,例如:

  • 没有
    -G
    (“y被x调用”):

  • 为什么
    \uu sin_sse2
    main
    (间接?)、
    foo
    bar
    调用,而不是被
    baz
    调用
  • 为什么函数有时会附加百分比和层次结构(例如,
    baz
    的最后一个实例),而有时不会附加百分比和层次结构(例如,
    bar
    的最后一个实例)
  • 使用
    -G
    (“x调用y”):

  • 我应该如何解释
    下的前三个条目
  • main
    调用
    foo
    没关系,但是如果它调用
    \uu sin\u sse2
    sin@plt
    (间接地?)它也不调用
    bar
    baz
  • 为什么
    \u libc\u start\u main
    main
    出现在
    foo
    下?为什么
    foo
    出现两次
怀疑的是,这个层次结构有两个层次,第二个层次实际上表示“x调用y”/“y被x调用”语义,但我已经猜累了,所以我在这里提问。而这些文档似乎也帮不上忙



很抱歉,这篇文章太长了,但我希望所有这些上下文也能对其他人有所帮助或起到参考作用。

好吧,让我们暂时忽略呼叫者和被呼叫者调用图之间的差异,主要是因为当我在我的机器上比较这两个选项之间的结果时,我只看到
kernel.kallsyms
DSO内部的效果,原因我不明白——我自己对此还比较陌生

我发现对于你的例子来说,阅读整棵树更容易一些。因此,使用
--stdio
,让我们看看
\uu sin\uSSE2
的整个树:

# Overhead    Command      Shared Object                  Symbol
# ........  .........  .................  ......................
#
    94.72%  perf-test  libm-2.19.so       [.] __sin_sse2
            |
            --- __sin_sse2
               |
               |--44.20%-- foo
               |          |
               |           --100.00%-- main
               |                     __libc_start_main
               |                     _start
               |                     0x0
               |
               |--27.95%-- baz
               |          |
               |          |--51.78%-- bar
               |          |          foo
               |          |          main
               |          |          __libc_start_main
               |          |          _start
               |          |          0x0
               |          |
               |           --48.22%-- foo
               |                     main
               |                     __libc_start_main
               |                     _start
               |                     0x0
               |
                --27.84%-- bar
                          |
                           --100.00%-- foo
                                     main
                                     __libc_start_main
                                     _start
                                     0x0
因此,我的阅读方式是:44%的时间,
sin
是从
foo
调用的;27%的时间是从
baz
调用的,27%的时间是从bar调用的

-g的文档具有指导意义:

 -g [type,min[,limit],order[,key]], --call-graph
       Display call chains using type, min percent threshold, optional print limit and order. type can be either:

       ·   flat: single column, linear exposure of call chains.

       ·   graph: use a graph tree, displaying absolute overhead rates.

       ·   fractal: like graph, but displays relative rates. Each branch of the tree is considered as a new profiled object.

               order can be either:
               - callee: callee based call graph.
               - caller: inverted caller based call graph.

               key can be:
               - function: compare on functions
               - address: compare on individual code addresses

               Default: fractal,0.5,callee,function.
这里重要的一点是默认值是分形,在分形模式下,每个分支都是一个新对象

因此,您可以看到,调用
baz
的时间有50%是从
bar
调用的,另外50%是从
foo
调用的

这并不总是最有用的度量,因此使用
-g graph
查看结果很有意义:

94.72%  perf-test  libm-2.19.so       [.] __sin_sse2
        |
        --- __sin_sse2
           |
           |--41.87%-- foo
           |          |
           |           --41.48%-- main
           |                     __libc_start_main
           |                     _start
           |                     0x0
           |
           |--26.48%-- baz
           |          |
           |          |--13.50%-- bar
           |          |          foo
           |          |          main
           |          |          __libc_start_main
           |          |          _start
           |          |          0x0
           |          |
           |           --12.57%-- foo
           |                     main
           |                     __libc_start_main
           |                     _start
           |                     0x0
           |
            --26.38%-- bar
                      |
                       --26.17%-- foo
                                 main
                                 __libc_start_main
                                 _start
                                 0x0
这将更改为使用绝对百分比,其中每个调用链的时间百分比都会被报告:因此
foo->bar
是总滴答声的26%(依次调用
baz
),而
foo->baz
(直接)是总滴答声的12%

我仍然不知道为什么从
\uu sin\u sse2
的角度看,我看不到被调用方和调用方图之间的任何差异

更新 我在命令行中做了一个更改,那就是调用图的收集方式。默认情况下,LinuxPerf使用帧指针方法重建调用堆栈。当编译器使用
-fomit帧指针
作为指针时,这可能是一个问题。所以我用

perf record --call-graph dwarf ./perf-test

我不是
perf
方面的专家,但我知道默认情况下,它每秒查看堆栈1000次以收集数据。因此,您尝试进行的细粒度分析可能会失败。例如,当
baz
调用
sin_sse2
时,可能没有任何样本发生。考虑使用<代码> GPRO,它编译存根来捕获每个调用和返回(虽然它还有其他问题)。是的,我知道,但是它很快,它让我记录了各种疯狂事件,例如每个符号的缓存错误和分支错误预测,而AFAIK <代码> GPROF不能这样做。我使用了
N
相当大,就是为了避免你提到的东西;无论如何,我试着进一步增加它,但没有运气,我不知道,但我认为,即使在100米的迭代循环中,也不太可能没有收集到样本。相对与绝对百分比的事情绝对有意义。主要的一点是,我无法得到与您类似的输出(这似乎是合法的,因为所有三个函数都直接调用了
\uu sinu sse2
,而我的功能是
foo
main
bar
)。你能发布你使用的确切标志吗?@cYrus看一下我的更新。。。午餐后将发布
perf report
的确切命令行。@MatthewG。嗯,看起来这一切都是关于使用
dwarf
。。。我将检查这是否解决了我列出的所有问题。@cYrus我复制并粘贴了用于编译的命令行(不过,我必须将链接器标志移到末尾)。内核是3.13.0-37-generic(这可能很重要)。报告的命令行系列是
perf report-g{graph,fractal},0.05,calle{e,r}--stdio>{graph,fractal}Cal
# Overhead    Command      Shared Object                  Symbol
# ........  .........  .................  ......................
#
    94.72%  perf-test  libm-2.19.so       [.] __sin_sse2
            |
            --- __sin_sse2
               |
               |--44.20%-- foo
               |          |
               |           --100.00%-- main
               |                     __libc_start_main
               |                     _start
               |                     0x0
               |
               |--27.95%-- baz
               |          |
               |          |--51.78%-- bar
               |          |          foo
               |          |          main
               |          |          __libc_start_main
               |          |          _start
               |          |          0x0
               |          |
               |           --48.22%-- foo
               |                     main
               |                     __libc_start_main
               |                     _start
               |                     0x0
               |
                --27.84%-- bar
                          |
                           --100.00%-- foo
                                     main
                                     __libc_start_main
                                     _start
                                     0x0
 -g [type,min[,limit],order[,key]], --call-graph
       Display call chains using type, min percent threshold, optional print limit and order. type can be either:

       ·   flat: single column, linear exposure of call chains.

       ·   graph: use a graph tree, displaying absolute overhead rates.

       ·   fractal: like graph, but displays relative rates. Each branch of the tree is considered as a new profiled object.

               order can be either:
               - callee: callee based call graph.
               - caller: inverted caller based call graph.

               key can be:
               - function: compare on functions
               - address: compare on individual code addresses

               Default: fractal,0.5,callee,function.
94.72%  perf-test  libm-2.19.so       [.] __sin_sse2
        |
        --- __sin_sse2
           |
           |--41.87%-- foo
           |          |
           |           --41.48%-- main
           |                     __libc_start_main
           |                     _start
           |                     0x0
           |
           |--26.48%-- baz
           |          |
           |          |--13.50%-- bar
           |          |          foo
           |          |          main
           |          |          __libc_start_main
           |          |          _start
           |          |          0x0
           |          |
           |           --12.57%-- foo
           |                     main
           |                     __libc_start_main
           |                     _start
           |                     0x0
           |
            --26.38%-- bar
                      |
                       --26.17%-- foo
                                 main
                                 __libc_start_main
                                 _start
                                 0x0
perf record --call-graph dwarf ./perf-test