性能工具perf的用法以及如何绘制性能火焰图
perf简介
Perf是内置于Linux内核源码树中的性能剖析(profiling)工具。其基于事件采样原理,以性能事件为基础,常用于性能瓶颈的查找与热点代码的定位。
性能调优工具如 perf,Oprofile 等的基本原理都是对被监测对象进行采样,最简单的情形是根据 tick 中断进行采样,即在 tick 中断内触发采样点,在采样点里判断程序当时的上下文。假如一个程序 90% 的时间都花费在函数 foo() 上,那么 90% 的采样点都应该落在函数 foo的上下文中。只要采样频率足够高,采样时间足够长,那么以上推论就比较可靠。因此,通过 tick 触发采样,我们便可以了解程序中哪些地方最耗时间,从而重点分析。
稍微扩展一下思路,就可以发现改变采样的触发条件使得我们可以获得不同的统计数据:
- 以时间点 ( 如 tick) 作为事件触发采样便可以获知程序运行时间的分布。
- 以 cache miss 事件触发采样便可以知道 cache miss 的分布,即 cache 失效经常发生在哪些程序代码中
- 等等其他事件
当然,perf使用更多是CPU的PMU计数器,PMU计数器是大部分CPU都有的功能,它们可以用来统计比如L1 Cache失效的次数,分支预测失败的次数等。PMU可以在这些计数器的计数超过一个特定的值的时候产生一个中断,这个中断,我们可以用和时钟一样的方法,来抽样判断系统中哪个函数发生了最多的Cache失效,分支预测失效等。
perf 用法
本文示例代码:
//
// Created by wilcohuang on 2018/11/19.
//
#include <unistd.h>
using namespace std;
#define NUM 500000
void init(int *int_array) {
for (int i = 0; i < NUM; i++) {
int_array[i] = i;
}
}
void accu(int *int_array, long &sum) {
for (int i = 0; i < NUM; i++) {
sum += int_array[i];
usleep(3);
}
}
int main() {
int int_array[NUM];
init(int_array);
long sum = 0;
accu(int_array, sum);
}
说明
perf的使用可以分为两种方式:
- 直接使用perf启动服务
- 挂接到已启动的进程
第一种方式不需要root权限,第二种方式需要root权限
perf top
用于查看cpu的主要性能消耗点
跟踪一个名为main的进程:
perf top -e cycles -p `pgrep main`
输入如下:
perf record
同样是分析诊断进程:
perf record -e cpu-clock -g ./run
或者
perf record -e cpu-clock -g -p 4522
使用ctrl+c中断perf进程,或者在程序执行结束后,会产生perf.data的文件,使用perf report
会产生结果分析,如图
火焰图
上面通过文件查看不够直观,还有一种火焰图分析的方式:
工具下载:git clone https://github.com/brendangregg/FlameGraph.git
使用命令:
使用perf script工具对perf.data进行解析perf script -i perf.data &> perf.unfold
将perf.unfold中的符号进行折叠:/data/stackcollapse-perf.pl perf.unfold &> perf.folded
最后生成svg图:/data/flamegraph.pl perf.folded > perf.svg
然后可以通过chrome或者看图软件打开:
Y轴表示调用栈,X轴越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。
所以,一般我们只需要看有没有出现 “平顶”,如果有,那么这个函数可能有性能问题。
perf diff
优化程序性能后,我们自然要看下效果:perf diff perf.data perf.data.before
我们干掉了上文中的usleep(3);
这句代码,然后diff结果输出如下:
init、accu函数的时间占比已经将为了0 ?
权限问题
perf如果不是root,你可以做什么取决于sysctl设置。kernel.perf_event_paranoid
cat /proc/sys/kernel/perf_event_paranoid
kernel.perf_event_paranoid= 2:您无法进行任何测量。该perf实用程序可能仍然是有用的分析现有的记录用perf ls,perf report,perf timechart或perf trace。
kernel.perf_event_paranoid= 1:您可以使用perf stator 跟踪命令perf record,并获取内核分析数据。
kernel.perf_event_paranoid= 0:您可以使用perf stat或跟踪命令perf record,并获取CPU事件数据。
kernel.perf_event_paranoid= -1:您获得了对内核跟踪点的原始访问权限(具体来说,您可以mmap创建文件perf_event_open,我不知道其含义是什么)。
参考
更全的perf参考:
本文参考: