【问题标题】:linux perf: how to interpret and find hotspotslinux perf:如何解释和查找热点
【发布时间】:2011-10-25 06:19:39
【问题描述】:

我今天试用了 linux 的 perf 实用程序,但在解释其结果时遇到了麻烦。我习惯了 valgrind 的 callgrind,这当然是完全不同的基于采样的 perf 方法。

我做了什么:

perf record -g -p $(pidof someapp)
perf report -g -n

现在我看到了这样的东西:

+ 16.92% kdevelop libsqlite3.so.0.8.6 [.] 0x3fe57 ↑ + 10.61% kdevelop libQtGui.so.4.7.3 [.] 0x81e344 ▮ + 7.09% kdevelop libc-2.14.so [.] 0x85804 ▒ + 4.96% kdevelop libQtGui.so.4.7.3 [.] 0x265b69 ▒ + 3.50% kdevelop libQtCore.so.4.7.3 [.] 0x18608d ▒ + 2.68% kdevelop libc-2.14.so [.] memcpy ▒ + 1.15% kdevelop [kernel.kallsyms] [k] copy_user_generic_string ▒ + 0.90% kdevelop libQtGui.so.4.7.3 [.] QTransform::translate(double, double) ▒ + 0.88% kdevelop libc-2.14.so [.] __libc_malloc ▒ + 0.85% kdevelop libc-2.14.so [.] memcpy ...

好的,这些函数可能很慢,但是我如何知道它们是从哪里调用的呢?由于所有这些热点都位于外部库中,因此我看不到优化代码的方法。

基本上,我正在寻找某种带有累积成本注释的调用图,其中我的函数比我调用的库函数具有更高的包容性采样成本。

这可以通过 perf 实现吗?如果是这样 - 如何?

注意:我发现“E”打开了调用图并提供了更多信息。但是调用图通常不够深和/或随机终止,而没有提供有关在哪里花费了多少信息的信息。示例:

- 10.26% 凯特 libkatepartinterfaces.so.4.6.0 [.] Kate::TextLoader::readLine(int&... 凯特::TextLoader::readLine(int&, int&) 凯特::TextBuffer::load(QString const&, bool&, bool&) KateBuffer::openFile(QString const&) 凯特文档::openFile() 0x7fe37a81121c

这可能是我在 64 位上运行的问题吗?另见:http://lists.fedoraproject.org/pipermail/devel/2010-November/144952.html(我没有使用 fedora,但似乎适用于所有 64 位系统)。

【问题讨论】:

  • 你使用的是什么版本的perf
  • 我使用的是 archlinux 的 AUR 的 perf,现在是 v3.0.3

标签: c++ linux performance profiling perf


【解决方案1】:

你应该试试热点: https://www.kdab.com/hotspot-gui-linux-perf-profiler/

在 github 上可用:https://github.com/KDAB/hotspot

例如,它能够为您生成火焰图。

【讨论】:

  • 确实如此。请注意,我是热点的主要作者 :) 我自己挠痒痒!
  • eclipse.org/linuxtools/projectPages/perf 上还有 eclipse linux-tools 集成
  • @milianw - 如果应用是大数据机器学习,那么hotspotperf record 有什么帮助?我观察到记录的大多数分析详细信息都与库相关,这些库是必需的,否则应用程序将无法运行。对于大数据应用程序,我应该使用hotspot 解释什么以及如何解释perf record 数据的任何指针?
【解决方案2】:

Linux 3.7 perf 终于可以使用 DWARF 信息来生成调用图了:

perf record --call-graph dwarf -- yourapp
perf report -g graph --no-children

整洁,但与 VTune、KCacheGrind 或类似工具相比,curses GUI 很糟糕……我建议尝试使用 FlameGraphs,这是一个非常简洁的可视化:http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html

注意:在报告步骤中,-g graph 使结果输出易于理解“相对于总数”的百分比,而不是“相对于父级”的数字。 --no-children 将仅显示自身成本,而不是包含成本 - 我也认为这是一个非常宝贵的功能。

如果您有新的 perf 和 Intel CPU,也可以试试 LBR unwinder,它具有更好的性能并生成更小的结果文件:

perf record --call-graph lbr -- yourapp

这里的缺点是调用堆栈深度与默认的 DWARF 展开器配置相比更加有限。

【讨论】:

  • code.google.com/p/jrfonseca/wiki/Gprof2Dot能够将性能输出转换为点图
  • 好消息!不过,我无法从中获得有用的东西。对于 debian 2.8 内核,您首先必须从安装了 libunwind 的内核树中重新编译“perf”工具才能获得此选项。我想对于大多数人来说,使用 -fno-omit-frame-pointer 重新编译东西会更容易。
  • 这不起作用(不再),必须提供另一个选项(--call-graph):perf record -g --call-graph dwarf -- yourapp 否则它将退出而不实际记录任何数据
  • 您可以使用FlameGraph 很好地可视化调用堆栈采样时发生的情况。
【解决方案3】:

您可以使用perf annotate 获得非常详细的源级报告,请参阅Source level analysis with perf annotate。它看起来像这样(无耻地从网站上窃取):

------------------------------------------------
 Percent |   Source code & Disassembly of noploop
------------------------------------------------
         :
         :
         :
         :   Disassembly of section .text:
         :
         :   08048484 <main>:
         :   #include <string.h>
         :   #include <unistd.h>
         :   #include <sys/time.h>
         :
         :   int main(int argc, char **argv)
         :   {
    0.00 :    8048484:       55                      push   %ebp
    0.00 :    8048485:       89 e5                   mov    %esp,%ebp
[...]
    0.00 :    8048530:       eb 0b                   jmp    804853d <main+0xb9>
         :                           count++;
   14.22 :    8048532:       8b 44 24 2c             mov    0x2c(%esp),%eax
    0.00 :    8048536:       83 c0 01                add    $0x1,%eax
   14.78 :    8048539:       89 44 24 2c             mov    %eax,0x2c(%esp)
         :           memcpy(&tv_end, &tv_now, sizeof(tv_now));
         :           tv_end.tv_sec += strtol(argv[1], NULL, 10);
         :           while (tv_now.tv_sec < tv_end.tv_sec ||
         :                  tv_now.tv_usec < tv_end.tv_usec) {
         :                   count = 0;
         :                   while (count < 100000000UL)
   14.78 :    804853d:       8b 44 24 2c             mov    0x2c(%esp),%eax
   56.23 :    8048541:       3d ff e0 f5 05          cmp    $0x5f5e0ff,%eax
    0.00 :    8048546:       76 ea                   jbe    8048532 <main+0xae>
[...]

编译代码时不要忘记传递-fno-omit-frame-pointer-ggdb 标志。

【讨论】:

    【解决方案4】:

    除非您的程序具有很少的函数并且几乎不调用系统函数或 I/O,否则对程序计数器进行采样的分析器不会告诉您太多信息,正如您所发现的那样。 事实上,著名的分析器 gprof 是专门创建的,以尝试解决仅自我分析的无用问题(并不是说它成功了)。

    真正起作用的是在挂钟时间(从而包括 I/O时间),并按行或按指令报告(从而确定您应该调查的函数调用,而不仅仅是它们所在的函数)。

    此外,您应该寻找的统计数据是堆栈时间百分比,而不是调用次数,而不是平均包含函数时间。 尤其不是“自我时间”。 如果调用指令(或非调用指令)有 38% 的时间在堆栈中,那么如果你可以摆脱它,你会节省多少? 38%! 很简单,不是吗?

    这种分析器的一个例子是Zoom

    关于这个主题有more issues to be understood

    添加:@caf 让我寻找 perf 信息,并且由于您包含了命令行参数 -g,它确实收集了堆栈样本。然后您可以获得call-tree 报告。 然后,如果您确保您在挂钟时间进行采样(这样您就可以得到等待时间和 CPU 时间),那么您几乎得到了您需要的东西。

    【讨论】:

    • perf 确实对调用堆栈进行采样。只是 OP 生成的报告只显示叶子。
    • @caf:我只是searched again 并知道如果您包含-g 选项,它会收集堆栈样本。我认为它可以在挂钟时间完成。 OP 应该会觉得这很有帮助。现在剩下的就是副行报告了。
    • @caf: 那我怎么显示整棵树呢?
    • @Ali:我刚刚检查了您的答案和链接。它是否需要堆栈样本,对于call 指令,它是否会在堆栈中的某处记录包含该调用的样本分数,并在文件 I/O 等自愿非处理时间进行采样。这些东西都是需要的,几乎不需要测量精度。我做了一个(非常业余的)video on this subject.
    • IMO、使用 perf 计数器的高精度/精确分析等有助于您准确了解为什么受 CPU 限制的热循环很慢,以及您如何可能会加快速度。描述事物的整体“时间花在哪里”是一个完全不同的问题。在那里,您可能希望重新设计事物以减少工作量,而不是调整循环以更快地完成相同的工作。对这个过程可能有用的是一种方法来描述复制内容所花费的缓存占用空间,而不仅仅是时间,这样您就可以找到并更改缓存污染但快速的函数。
    【解决方案5】:

    好的,这些函数可能很慢,但是我如何知道它们是从哪里调用的呢?由于所有这些热点都位于外部库中,因此我看不到优化代码的方法。

    您确定您的应用程序someapp 是使用gcc 选项-fno-omit-frame-pointer(可能还有它的依赖库)构建的吗? 像这样的:

    g++ -m64 -fno-omit-frame-pointer -g main.cpp
    

    【讨论】:

    • 我认为这个答案为时已晚,无法获得所有支持,因为-fno-omit-frame-pointer 是分析中最重要的事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-23
    • 2022-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多