我用了perf
使用perf record ./application
生成一个perf文件。 perf report
向我展示了有关它的各种信息。如何显示运行应用程序所需的总时间以及运行特定“符号”/功能的总时间? perf 似乎经常显示百分比,但我想要原始时间,并且它想要“包容性”时间,即包括 child 。
Ubuntu Linux 18.04 上的 perf v4.15.18
最佳答案
perf 是 statistical (sampling) profiler(在其默认的 perf record
模式下),这意味着它在函数进入和退出时没有精确的时间戳(精确数据需要跟踪)。 Perf 要求操作系统内核每秒生成数千次中断(如果支持 -e Cycles
,则硬件 PMU 为 4 kHz,对于软件事件 -e cpu-clock
则更少)。程序执行的每次中断都被记录为样本,其中包含EIP(当前指令指针)、pid(进程/线程id)、当前时间的时间戳。当程序运行几秒钟时,会有数千个样本,perf report
可以根据它们生成直方图:程序代码的哪些部分(哪些函数)比其他部分执行得更频繁。您将得到一般性的概览,即某些函数确实占用了大约 30% 的程序执行时间,而其他函数则占用了 5%。
perf 报告不计算总的程序执行时间(它可以通过比较第一个和最后一个样本的时间戳来估计它,但如果存在关闭 CPU 周期,则它是不准确的)。但它确实估计了总事件计数(它在交互式 TUI 中打印在第一行,并在文本输出中列出):
$ perf report |grep approx
# Samples: 1K of event 'cycles'
# Event count (approx.): 844373507
有 perf report -n
选项,可在百分比列旁边添加“样本数”列。
Samples: 1K of event 'cycles', Event count (approx.): 861416907
Overhead Samples Command Shared Object Symbol
42.36% 576 bc bc [.] _bc_rec_mul
37.49% 510 bc bc [.] _bc_shift_addsub.isra.3
14.90% 202 bc bc [.] _bc_do_sub
0.89% 12 bc bc [.] bc_free_num
但是采样的时间间隔不同,并且不如计算的开销精确(每个样本可能具有不同的权重)。我建议您运行 perf stat ./application
以获得应用程序的实际总运行时间和总硬件计数。当您的应用程序具有稳定的运行时间时会更好(执行 perf stat -r 5 ./application
以使工具估计的变化为最后一列中的“+- 0.28%”)
要包含子函数,必须在每次中断时对堆栈跟踪进行采样。它们不会在默认的性能记录模式下进行采样。使用 -g
或 --call-graph dwarf
选项打开此采样:perf record -g ./application
或 perf记录--call-graph dwarf ./application
。在 Linux 中正确使用它来预装库或应用程序并不简单(因为大多数发行版都会从包中删除调试信息),但可以用于您自己使用调试信息编译的应用程序。默认的 -g
与 --call-graph fp
相同,要求所有代码都使用 -fno-omit-frame-pointer
进行编译gcc 选项,并且非默认的 --call-graph dwarf
更可靠。通过正确准备的程序和库、单线程应用程序以及足够长的堆栈大小示例(默认为 8KB,可使用 --call-graph dwarf,65536
进行更改)、性能报告
应显示 _start
和 main
函数(包括子函数)的 99% 左右。
使用-fno-omit-frame-pointer编译的bc计算器
:
bc-no-omit-frame$ echo '3^123456%3' | perf record -g bc/bc
bc-no-omit-frame$ perf report
Samples: 1K of event 'cycles:uppp', Event count (approx.): 811063902
Children Self Command Shared Object Symbol
+ 98.33% 0.00% bc [unknown] [.] 0x771e258d4c544155
+ 98.33% 0.00% bc libc-2.27.so [.] __libc_start_main
+ 98.33% 0.00% bc bc [.] main
带有 dwarf 调用图的 bc 计算器:
$ echo '3^123456%3' | perf record --call-graph dwarf bc/bc
$ perf report
Samples: 1K of event 'cycles:uppp', Event count (approx.): 898828479
Children Self Command Shared Object Symbol
+ 98.42% 0.00% bc bc [.] _start
+ 98.42% 0.00% bc libc-2.27.so [.] __libc_start_main
+ 98.42% 0.00% bc bc [.] main
没有调试信息的 bc 在 -g
(fp) 模式下由 perf 进行不正确的调用图处理(main 没有 99%):
$ cp bc/bc bc.strip
$ strip -d bc.strip
$ echo '3^123456%3' | perf record --call-graph fp ./bc.strip
Samples: 1K of event 'cycles:uppp', Event count (approx.): 841993392
Children Self Command Shared Object Symbol
+ 43.94% 43.94% bc.strip bc.strip [.] _bc_rec_mul
+ 39.73% 39.73% bc.strip bc.strip [.] _bc_shift_addsub.isra.3
+ 11.27% 11.27% bc.strip bc.strip [.] _bc_do_sub
+ 0.92% 0.92% bc.strip libc-2.27.so [.] malloc
有时,perf report --no-children
可用于禁用对 self+children 开销进行排序(将按“self”开销排序),例如,当未完全捕获调用图时。
关于performance - 查询 `perf.data` 文件以获取符号的总原始执行时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62860433/