BLOG

Record, summarize, and improve.

perf性能分析

一、perf总览

系统级性能优化通常包含两个阶段:性能剖析、代码优化

性能剖析是寻找性能瓶颈,查找引发性能问题的原因及热点代码

代码优化则是针对具体的性能问题而进行代码优化

perf是性能剖析阶段的瑞士军刀,目前已经包含在Linux内核源码中,主要用于对整个系统进行统计分析,通常perf工具由前端和后端两部分组成,前端在用户层空间负责将采集的指令、数据类型、频率等一系列参数传递给后端,后端在内核层拿到这些参数再去内核中采集相应的模块和数据,perf最强大的地方在于既能够对系统整体的性能进行分析,也能对线程级的性能进行剖析,从而全面了解系统的性能,对系统做一个评估。因为是对过程的采样,所以数据相对连续采集来说会减小很多。perf最主要的用途就是对系统进行采样,进而分析执行过程中瓶颈所在。

perf 属于一个命令的集合,包含很多子命令:

annotate        注释读取perf.data(由perf record创建)并显示带注释的代码
archive         在perf.data文件中找到的带有构建ID的目标文件创建存档
bench           基准套件的通用框架,针对内存和调度子系统
buildid-cache   管理build-id缓存。
buildid-list    列出在perf.data文件中的buildid
c2c             cache-2-cache 和 cacheline 错误共享分析
config          在配置文件中获取并设置变量。
data            数据文件相关处理
diff            读取perf.data文件并显示差异配置文件
evlist          列出在perf.data文件中的事件名称
ftrace          用于内核ftrace功能的简单包装器
inject          注入过滤器以增加事件流的附加信息
kallsyms        在运行的内核中搜索符号
kmem            内核内存分配分析
kvm             跟踪/测量guest os的kvm
list            列出所有符号事件类型
lock            分析锁事件
mem             配置文件内存访问
record          运行命令并将其概要文件记录到perf.data中
report          从perf.data (created by perf record) 读取性能数据并显示配置文件
sched           内核调度程序统计信息
script          从perf.data (created by perf record) 读取性能数据并显示跟踪输出
stat            运行命令并收集性能计数器统计信息
test            完整性测试
timechart       可视化工作负载期间整个系统行为的工具
top             系统分析工具
version         版本信息
probe           定义新的动态跟踪点
trace           strace启动工具

每一个子命令下面还有子命令和执行参数,所以perf的功能非常强大。

二、采样

perf最突出的特征是能够对系统中进行采样,使用record命令能够设置采样频率并记录此刻开始之后系统的行为和事件,例如是否发生了调度、是否进行内存的分配等,perf支持的事件可以分为三类,使用perf list可以列出所有的事件:

  • 硬件事件:包括Hardware event、Hardware cache event、Kernel PMU event、Raw hardware event descriptor
  • 软件事件:包括Software event、Hardware breakpoint
  • Tracepoint事件:由各种Tracepoint event组成

正是因为perf能够支持这些事件,所以它能够对整个系统进行剖析。使用perf能够进行各种采样:

perf record对系统中的各种函数或某个进程的执行流程进行采样,之后可以通过perf script生成函数调用关系,通过perf report将数据导出,并分析每个函数采样的次数结合时间戳能够直接发现是哪个函数导致的性能问题,但问题是导出的数据量一旦增加就很难发现瓶颈处。所以后续开发出了火焰图用于分析perf record记录的函数采用关系。火焰图需要利用perf script生成的函数栈,通过将其中的符号进行折叠,最后将其导出成一幅标准格式的图片,Brendan Gregg大佬已经将这些工作写成了脚本,我们只需要调用即可。在execl测试中,通过perf record采集到是CPU上执行的函数

Image in a image block

通过火焰图能够轻易的看出哪些情况执行的时间较长,在火焰图上横轴较长的就是我们要考虑优化的点。

但是有时候单纯从CPU运行的角度来看并不能发现因等待事件而导致挂起的延迟事件,例如因等待IO而导致被调度,这时候需要从另外一个角度来看,也就是不在CPU上运行的时间。perf虽然没有直接检测睡眠等事件的直接命令,但是可以通过'sched:sched_switch'、'sched:sched_stat_sleep'、'sched:sched_stat_blocked'等事件来记录发生调度和睡眠的时机,再通过同样的方式用火焰图画出,就形成了OFF-CPU火焰图

Image in a image block

OFF-CPU火焰图用来分析等待IO、锁等事件的触发,这些事件的触发使CPU进入等待状态。其实不仅仅是上面两种火焰图,还可以将两个火焰图进行比较,从而生成一种全新的火焰图,红蓝差分火焰图,对两幅火焰图进行对比,高出的地方将显示红色,而低于的地方显示蓝色,这种火焰图使用与新系统引起性能的下降,通过对比,一眼就能看出其不足的地方。还有其他的火焰图,例如内存火焰图,用来分析内存的分配。其实只要采样的条件不同,可以生成各式各样的火焰图,具体情况需要根据场景选择。

三、perf其他实用的工具

perf最强大的虽然是采样功能,通过对采样结果进行过滤也能到到不同的效果,再对输出的结果进行图形化显示就诞生了其他的工具。

perf sched:

分析系统中每一次的调度,sched工具能够抓取系统中的每一次调度所发生的时机,从而明确看出所有程序的执行流程。执行时需要先使用perf sched record 对系统进行采样,可以以时间流和统计方式显示调度事件:

统计所有进程所发生调度的次数,能够看到每个进程执行了多长时间

Image in a image block

统计每一次的调度,从左到右每一列代表每一个CPU,*符号代表发生了一次调度

Image in a image block

甚至是每一次调度发生的原因

Image in a image block

每个进程等待了多长时间,调度发生的时间,以及运行的时间

Image in a image block

perf stat:

perf stat与time类似,能够检测命令在用户层和系统层分别执行了多长时间,还能够探测过程中触发了哪些事件

Image in a image block

perf kmem:

kmem能够探测系统的内存相关事件,例如常见的page、slab、alloc等内存事件,

Image in a image block
Image in a image block

perf top:

perf top与top命令相似,能够所有进程占用CPU的资源,而且能够查看每个进程中最热点的汇编语句,从而直接看出哪里是关键点

Image in a image block

perf timechart:

timechart能够直接以图表的形式显示所有CPU的运行,以及是哪个进程在哪个CPU上运行,某个时间点哪个CPU或进程处于什么状态

Image in a image block

即使现有的perf事件不够使用,还可以通过perf probe调用内核中的kprobe和用户层的uprobe添加动态探针,从而对系统进行探测,具体实现参考网上教程。有一些命令是为了对采样得到的数据进行处理,例如evlist能够检测通过record生成的perf文件中包含哪些事件,diff能够对比当前的事件与record生成的perf文件中有哪些事件不一样,buildid-cache则与缓存相关,bench下面还有很多关于内存和调度的一些选项用来探测系统中的内存和调度事件。

最后,使用perf是会对系统的性能产生影响的,所以小的测试可以全部跟踪,大的程序应该对其切片,截取其中的一段时间的样本进行分析即可。