linux perf:如何解释和查找热点

我今天尝试了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 
 ...

好的,这些function可能会很慢,但我怎么知道他们从哪里来的? 由于所有这些热点在外部库中,我看不出优化我的代码。

基本上我正在寻找某种以累计成本为标注的调用图,其中我的function比我所称的库函数具有更高的包容性采样成本。

这是可能的性能? 如果是这样 – 如何?

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

 -  10.26%kate libkatepartinterfaces.so.4.6.0 [。] Kate :: TextLoader :: readLine(int&...
      Kate :: TextLoader :: readLine(int&,int&)                                            
      Kate :: TextBuffer :: load(QString const&,bool&,bool&)                              
      KateBuffer :: openFile(QString const&)                                              
      KateDocument ::中openFile()                                                          
      0x7fe37a81121c

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

Solutions Collecting From Web of "linux perf:如何解释和查找热点"

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

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

例如,它可以为你生成火焰图。

flamegraph

在Linux 3.7中,perf最终能够使用DWARF信息来生成callgraph:

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

整洁,但诅咒的图形用户界面相比,VTune,KCacheGrind或类似的可怕…我建议尝试FlameGraphs,而不是一个相当整洁的可视化: http ://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html

注意:在报告步骤中, -g graph使结果输出简单易懂,“相对于总数”百分比,而不是“相对于父级”数字。 --no-children只会显示自己的成本,而不是包容性的成本 – 我也觉得非常宝贵的一个特点。

如果你有一个新的perf和Intel CPU,也可以试试LBR unwinder,它的性能要好得多,而且产生的结果文件要小得多:

 perf record --call-graph lbr -- yourapp 

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

好的,这些功能可能会很慢,但我怎么知道他们从哪里来的? 由于所有这些热点在外部库中,我看不出优化我的代码。

你确定你的应用程序someapp是用gcc选项-fno-omit-frame-pointer (也可能是它的依赖库) -fno-omit-frame-pointer吗? 像这样的东西:

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

您可以使用perf annotate获取非常详细的源代码级别报告,请参阅使用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标志。

除非你的程序功能非常少,几乎没有调用过系统函数或I / O,否则正如你所发现的那样,对程序计数器进行采样的分析器不会告诉你很多。 事实上,众所周知的profiler gprof是专门设计来解决仅用于自定义分析的无用性(不是成功的)。

实际工作的是采样调用堆栈 (从而找出调用来自何处), 挂钟时间(从而包括I / O时间)以及按行或按指令报告(从而查明函数调用你应该调查,而不仅仅是他们居住的功能)。

此外,您应该查找的统计信息是堆栈中的时间百分比 ,而不是调用的数量,而不是平均包含的功能时间。 尤其不是“自我时间”。 如果一个呼叫指令(或一个非呼叫指令)在堆栈38%的时间,那么如果你能摆脱它,你会节省多少? 38%! 很简单,不是吗?

这种分析器的一个例子是缩放 。

在这个问题上有更多的问题需要了解 。

补充:@caf让我狩猎perf信息,因为你包括命令行参数-g它确实收集堆栈样本。 然后你可以得到一个呼叫树报告。 那么如果你确定你是在墙上时钟的样本(所以你得到等待时间以及CPU时间),那么你几乎已经得到了你所需要的。