说明
总结perf在linux下进行性能分析的常用方式。
perf介绍
Perf是一款linux性能分析工具。
通过它,应用程序可以利用 PMU(performance monitoring unit),tracepoint和内核中的特殊计数器来进行性能统计。它不但可以分析指定应用程序的性能问题,也可以用来分析内核的性能问题,当然也可以同时分析应用代码和内核,从而全面理解应用程序中的性能瓶颈。
perf举例
perf stat
perf stat
命令用于检测和汇总关键CPU计数,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| xuchen03@pm02-bot04-30002:~$ perf stat gzip perf.data
Performance counter stats for 'gzip perf.data':
87.275950 task-clock (msec) # 0.995 CPUs utilized 0 context-switches # 0.000 K/sec 0 cpu-migrations # 0.000 K/sec 127 page-faults # 0.001 M/sec 152,861,746 cycles # 1.751 GHz 77,476,340 stalled-cycles-frontend # 50.68% frontend cycles idle <not supported> stalled-cycles-backend 201,679,601 instructions # 1.32 insns per cycle # 0.38 stalled cycles per insn 39,663,860 branches # 454.465 M/sec 1,397,981 branch-misses # 3.52% of all branches
0.087739461 seconds time elapsed
|
注意:在虚拟机下可能不支持某些功能
这里面有包含一个计数:每个指令周期执行的指令数(IPC),即insns per cycle
,这是一个常用的检测单位,高的IPC意味着高的CPU占用,底的IPC意味着更多的等待消耗(磁盘I/O,内存延迟)。
perf stat
的使用需要对CPU体系的知识,一般如果程序在代码,算法层面都没有地方在提升的时候可以考虑深挖下这边的性能,例如brach-miss,cache-miss(我的机器上不支持)。用的比较多的还是通过perf record
分析代码层面的性能问题。
perf record
perf record
命令通过一定频率采样CPU当前执行的堆栈,生成一个分析文件(perf.data)进行分析。如果某个函数比较耗时,那它占用CPU的时间就会越高,从而被采样到的概率也就越高。
perf report
用来统计采样文件(perf.data),生成一个较可观的输出(但是跟火焰图的直观还是没法比)
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void longa() { int i,j; for(i = 0; i < 10000; i++) j=i; } void foo2() { int i; for(i=0 ; i < 10000; i++) longa(); } void foo1() { int i; for(i = 0; i< 10000; i++) longa(); } int main(void) { foo1(); foo2(); }
|
执行命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| xuchen03@pm02-bot04-30002:~$ gcc test.c xuchen03@pm02-bot04-30002:~$ sudo perf record -g ./a.out [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.410 MB perf.data (~17900 samples) ] xuchen03@pm02-bot04-30002:~$ sudo perf report --stdio # To display the perf.data header info, please use --header/--header-only options. # # Samples: 2K of event 'cycles' # Event count (approx.): 1413222776 # # Children Self Command Shared Object Symbol # ........ ........ ....... ................. ......................... # 99.92% 0.00% a.out libc-2.19.so [.] __libc_start_main | --- __libc_start_main
99.92% 0.00% a.out a.out [.] main | --- main __libc_start_main
99.88% 99.88% a.out a.out [.] longa | --- longa | |--50.07%-- foo2 | main | __libc_start_main | |--49.89%-- foo1 | main | __libc_start_main --0.04%-- [...]
50.02% 0.00% a.out a.out [.] foo2 | --- foo2 main __libc_start_main
49.87% 0.04% a.out a.out [.] foo1 | --- foo1 main __libc_start_main
0.06% 0.06% a.out ld-2.19.so [.] do_lookup_x | --- do_lookup_x
0.03% 0.00% a.out [unknown] [k] 0x00007f9d7dc06317 | --- 0x7f9d7dc06317
|
可以看到self列占用时间最大的是longa,占比99.88%。堆栈也能看到,但是看起来不是很直观。
火焰图
火焰图以一种更直观的方式来看perf record
的采样结果
安装
安装很简单,直接使用git clone
即可
1 2
| xuchen03@pm02-bot04-30002:~$ cd ~ xuchen03@pm02-bot04-30002:~$ git clone https://github.com/brendangregg/FlameGraph.git
|
使用
继续刚刚的采样命令:
1 2 3 4 5 6
| xuchen03@pm02-bot04-30002:~$ gcc test.c xuchen03@pm02-bot04-30002:~$ sudo perf record -ga ./a.out [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.410 MB perf.data (~17900 samples) ] xuchen03@pm02-bot04-30002:~$ sudo perf report --stdio xuchen03@pm02-bot04-30002:~$ sudo perf script |cat|c++filt|~/FlameGraph/stackcollapse-perf.pl|~/sysperf/FlameGraph/flamegraph.pl > ~/c.svg
|
在自己的home目录下生成了c.svg,可以直接用浏览器打开:
可以很清楚的看到每个函数占用CPU的比例,以及这个函数的堆栈情况。在火焰图上可以很方便的找到哪个函数是可能是性能瓶颈,从而可以进一步的对这个函数进行分析。
动态追踪
perf更强大的地方是可以自定义追踪点
perf stat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| xuchen03@pm02-bot04-30002:~$ sudo perf probe -x a.out --add longa Added new event: probe_a:longa (on longa in /home/xuchen03/a.out)
You can now use it in all perf tools, such as:
perf record -e probe_a:longa -aR sleep 1
xuchen03@pm02-bot04-30002:~$ sudo perf stat -e probe_a:longa ./a.out
Performance counter stats for './a.out':
20,000 probe_a:longa
0.754663782 seconds time elapsed
|
可以看到stat能统计出来追踪函数longa被调用2W次
perf record
同时动态追踪和perf record
一起使用,可以进一步分析追踪点相关的采样情况(注意这里的perf record
加了–call-graph dwarf,不加可能导致堆栈不全)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| xuchen03@pm02-bot04-30002:~$ sudo perf record --call-graph dwarf -e probe_a:longa ./a.out [ perf record: Woken up 646 times to write data ] [ perf record: Captured and wrote 161.319 MB perf.data (~7048126 samples) ] xuchen03@pm02-bot04-30002:~$ sudo perf report --stdio # To display the perf.data header info, please use --header/--header-only options. # # Samples: 20K of event 'probe_a:longa' # Event count (approx.): 20000 # # Children Self Command Shared Object Symbol # ........ ........ ....... ............. ..................... # 100.00% 0.00% a.out a.out [.] _start | --- _start
100.00% 0.00% a.out libc-2.19.so [.] __libc_start_main | --- __libc_start_main _start
100.00% 0.00% a.out a.out [.] main | --- main __libc_start_main _start
100.00% 100.00% a.out a.out [.] longa | --- longa | |--50.00%-- foo1 | main | __libc_start_main | _start | --50.00%-- foo2 main __libc_start_main _start
50.00% 0.00% a.out a.out [.] foo1 | --- foo1 main __libc_start_main _start
50.00% 0.00% a.out a.out [.] foo2 | --- foo2 main __libc_start_main _start
|
因为本来longa函数在使用动态追踪之前就是占用100%,所以与之前的结果差不多。对于我们一般的程序来说,就会删除大量其他函数调用,只会得到追踪点相关的调用情况。
同样对于perf record
结果可以使用命令火焰图命令生成更直观的火焰图来查看
更强大的动态追踪
动态追踪更强大的地方可以追加追踪的变量,就像gdb调试程序一样方便。
例如我们这里给foo2的for循环里加追踪点,并追踪i变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| xuchen03@pm02-bot04-30002:~$ sudo perf probe -x a.out --add 'test.c:12 i' Added new event: probe_a:foo2 (on @test.c:12 in /home/xuchen03/a.out with i)
You can now use it in all perf tools, such as:
perf record -e probe_a:foo2 -aR sleep 1 xuchen03@pm02-bot04-30002:~$ sudo perf record -e probe_a:foo2 ./a.out [ perf record: Woken up 3 times to write data ] [ perf record: Captured and wrote 0.699 MB perf.data (~30527 samples) ] xuchen03@pm02-bot04-30002:~$ sudo perf script a.out 9082 [017] 21495626.717550: probe_a:foo2: (4004e9) i=0 a.out 9082 [017] 21495626.717601: probe_a:foo2: (4004e9) i=1 a.out 9082 [017] 21495626.717639: probe_a:foo2: (4004e9) i=2 a.out 9082 [017] 21495626.717676: probe_a:foo2: (4004e9) i=3 a.out 9082 [017] 21495626.717713: probe_a:foo2: (4004e9) i=4 a.out 9082 [017] 21495626.717750: probe_a:foo2: (4004e9) i=5 a.out 9082 [017] 21495626.717787: probe_a:foo2: (4004e9) i=6 a.out 9082 [017] 21495626.717824: probe_a:foo2: (4004e9) i=7 a.out 9082 [017] 21495626.717861: probe_a:foo2: (4004e9) i=8 a.out 9082 [017] 21495626.717898: probe_a:foo2: (4004e9) i=9 a.out 9082 [017] 21495626.717936: probe_a:foo2: (4004e9) i=10 a.out 9082 [017] 21495626.717973: probe_a:foo2: (4004e9) i=11 a.out 9082 [017] 21495626.718010: probe_a:foo2: (4004e9) i=12 a.out 9082 [017] 21495626.718047: probe_a:foo2: (4004e9) i=13 a.out 9082 [017] 21495626.718084: probe_a:foo2: (4004e9) i=14 a.out 9082 [017] 21495626.718121: probe_a:foo2: (4004e9) i=15 a.out 9082 [017] 21495626.718158: probe_a:foo2: (4004e9) i=16 a.out 9082 [017] 21495626.718195: probe_a:foo2: (4004e9) i=17 a.out 9082 [017] 21495626.718232: probe_a:foo2: (4004e9) i=18 a.out 9082 [017] 21495626.718269: probe_a:foo2: (4004e9) i=19 a.out 9082 [017] 21495626.718306: probe_a:foo2: (4004e9) i=20 a.out 9082 [017] 21495626.718343: probe_a:foo2: (4004e9) i=21 a.out 9082 [017] 21495626.718380: probe_a:foo2: (4004e9) i=22 a.out 9082 [017] 21495626.718417: probe_a:foo2: (4004e9) i=23 a.out 9082 [017] 21495626.718454: probe_a:foo2: (4004e9) i=24 a.out 9082 [017] 21495626.718491: probe_a:foo2: (4004e9) i=25 a.out 9082 [017] 21495626.718528: probe_a:foo2: (4004e9) i=26 a.out 9082 [017] 21495626.718565: probe_a:foo2: (4004e9) i=27 a.out 9082 [017] 21495626.718602: probe_a:foo2: (4004e9) i=28 a.out 9082 [017] 21495626.718639: probe_a:foo2: (4004e9) i=29 a.out 9082 [017] 21495626.718676: probe_a:foo2: (4004e9) i=30 a.out 9082 [017] 21495626.718713: probe_a:foo2: (4004e9) i=31 a.out 9082 [017] 21495626.718750: probe_a:foo2: (4004e9) i=32 a.out 9082 [017] 21495626.718787: probe_a:foo2: (4004e9) i=33 a.out 9082 [017] 21495626.718824: probe_a:foo2: (4004e9) i=34 a.out 9082 [017] 21495626.718861: probe_a:foo2: (4004e9) i=35 a.out 9082 [017] 21495626.718898: probe_a:foo2: (4004e9) i=36 a.out 9082 [017] 21495626.718935: probe_a:foo2: (4004e9) i=37 a.out 9082 [017] 21495626.718972: probe_a:foo2: (4004e9) i=38 a.out 9082 [017] 21495626.719009: probe_a:foo2: (4004e9) i=39 a.out 9082 [017] 21495626.719046: probe_a:foo2: (4004e9) i=40 a.out 9082 [017] 21495626.719083: probe_a:foo2: (4004e9) i=41 a.out 9082 [017] 21495626.719120: probe_a:foo2: (4004e9) i=42 a.out 9082 [017] 21495626.719157: probe_a:foo2: (4004e9) i=43 a.out 9082 [017] 21495626.719194: probe_a:foo2: (4004e9) i=44 a.out 9082 [017] 21495626.719231: probe_a:foo2: (4004e9) i=45 a.out 9082 [017] 21495626.719268: probe_a:foo2: (4004e9) i=46 a.out 9082 [017] 21495626.719305: probe_a:foo2: (4004e9) i=47 a.out 9082 [017] 21495626.719343: probe_a:foo2: (4004e9) i=48 a.out 9082 [017] 21495626.719380: probe_a:foo2: (4004e9) i=49 ...
|
参考
性能分析界的牛人: http://www.brendangregg.com/perf.html
中文介绍perf较不错的技术文章,我的例子代码摘自这里: https://www.ibm.com/developerworks/cn/linux/l-cn-perf1/index.html