简介
Perf全名是Performance Event,是在Linux 2.6.31以后内建的系统效能分析工具,依靠perf,应用程式可以利用PMU (Performance Monitoring Unit), tracepoint和核心内部的特殊计数器(counter)来进行统计,另外还能同时分析运行中的核心程式码,从而更全面了解应用程式中的效能瓶颈。
perf基本原理是对目标进行取样,纪录特定的条件下所侦测的事件是否发生以及发生的次数。例如根据tick中断进行取样,即在tick中断内触发取样点,在取样点里判断行程(process)当时的context。假如一个行程90%的时间都花费在函式foo()上,那么90%的取样点都应该落在函式foo()的上下文中。
Perf 可取样的事件非常多,可以分析:
-
Hardware event,如cpu-cycles、instructions 、cache-misses、branch-misses
-
Software event,如page-faults、context-switches
-
Tracepoint event
知道了cpu-cycles、instructions 我们可以了解Instruction per cycle 是多少,进而判断程式码有没有好好利用CPU,cache-misses 可以晓得是否有善用Locality of reference ,branch-misses 多了是否导致严重的pipeline hazard ?另外Perf 还可以对函式进行采样,了解效能卡在哪边。
背景知识
硬件Cache
内存读写是很快的,但还是无法和处理器的指令执行速度相比。为了从内存中读取指令和数据,处理器需要等待,用处理器的时间来衡量,这种等待非常漫长。Cache 是一种 SRAM,它的读写速率非常快,能和处理器处理速度相匹配。因此将常用的数据保存在 cache 中,处理器便无须等待,从而提高性能。Cache 的尺寸一般都很小,充分利用 cache 是软件调优非常重要的部分。
CPU Cache相关的文章:
硬件特性之流水线,超标量体系结构,乱序执行
提高性能最有效的方式之一就是并行。处理器在硬件设计时也尽可能地并行,比如流水线,超标量体系结构以及乱序执行。
处理器处理一条指令需要分多个步骤完成,比如先取指令,然后完成运算,最后将计算结果输出到总线上。在处理器内部,这可以看作一个三级流水线。
指令从左边进入处理器,上图中的流水线有三级,一个时钟周期内可以同时处理三条指令,分别被流水线的不同部分处理。
超标量(superscalar)指一个时钟周期发射多条指令的流水线机器架构,比如 Intel 的 Pentium 处理器,内部有两个执行单元,在一个时钟周期内允许执行两条指令。
此外,在处理器内部,不同指令所需要的处理步骤和时钟周期是不同的,如果严格按照程序的执行顺序执行,那么就无法充分利用处理器的流水线。因此指令有可能被乱序执行。
上述三种并行技术对所执行的指令有一个基本要求,即相邻的指令相互没有依赖关系。假如某条指令需要依赖前面一条指令的执行结果数据,那么 pipeline 便失去作用,因为第二条指令必须等待第一条指令完成。因此好的软件必须尽量避免这种代码的生成。
分支指令对软件性能有比较大的影响。尤其是当处理器采用流水线设计之后,假设流水线有三级,当前进入流水的第一条指令为分支指令。假设处理器顺序读取指令,那么如果分支的结果是跳转到其他指令,那么被处理器流水线预取的后续两条指令都将被放弃,从而影响性能。为此,很多处理器都提供了分支预测功能,根据同一条指令的历史执行记录进行预测,读取最可能的下一条指令,而并非顺序读取指令。
分支预测对软件结构有一些要求,对于重复性的分支指令序列,分支预测硬件能得到较好的预测结果,而对于类似 switch case 一类的程序结构,则往往无法得到理想的预测结果。
上面介绍的几种处理器特性对软件的性能有很大的影响,然而依赖时钟进行定期采样的 profiler 模式无法揭示程序对这些处理器硬件特性的使用情况。处理器厂商针对这种情况,在硬件中加入了 PMU 单元,即 performance monitor unit。
PMU 允许软件针对某种硬件事件设置 counter,此后处理器便开始统计该事件的发生次数,当发生的次数超过 counter 内设置的值后,便产生中断。比如 cache miss 达到某个值后,PMU 便能产生相应的中断。
捕获这些中断,便可以考察程序对这些硬件特性的利用效率了。
Tracepoints
Tracepoint 是散落在内核源代码中的一些 hook,一旦使能,它们便可以在特定的代码被运行到时被触发,这一特性可以被各种 trace/debug 工具所使用。Perf 就是该特性的用户之一。
假如您想知道在应用程序运行期间,内核内存管理模块的行为,便可以利用潜伏在 slab 分配器中的 tracepoint。当内核运行到这些 tracepoint 时,便会通知 perf。
Perf 将 tracepoint 产生的事件记录下来,生成报告,通过分析这些报告,调优人员便可以了解程序运行时期内核的种种细节,对性能症状作出更准确的诊断。
Perf使用
前面提到的,Perf可以触发的时间大致分为3类,包含20 几种子工具集,通过 help 获取一手资料。
1 | perf help top/mem/bench.. |
本文结合程序来进行perf使用的总结,用到的第一段程序代码perf_test.c
如下:
1 | int add(int i) { |
上面程序的内容很容易发现是个死循环,这样的代码运行时,会很消耗系统性能,下面根据本程序来系统总结Perf的日常使用:
perf list
perf list 应该是大多数人安装完perf的第一个命令,能列出perf能触发的event,按照第一段介绍的,主要是三类event:
1 | alignment-faults [Software event] |
perf top
perf top类似Linux内建的top指令。它能够实时分析各个函式在某个event上的热点,找出拖慢系统的凶手,即使没有特定的程序要观察,你也可以直接下达perf top
指令来观察是什么程序吃掉系统效能,导致系统异常变慢。
可以发现红色热点就出现了。右边第一列为各函式的符号,左边第一行是该符号引发的event在整个"监视域"中占的比例,我们称作该符号的热度,监视域指的是perf监控的所有符号,预设值包括系统所有程序、核心以及核心module的函式,左边第二行则为该符号所在的Shared Object 。若符号旁显示[.]
表示其位于User mode,[k]
则为kernel mode。
按下h
可以呼叫help ,它会列出perf top的所有功能和对应按键。我们来试看看Annotate(注解),这功能可以进一步深入分析某个符号。通常光标所在的行事件回车两次也是"Annotate"。
下图是上面程序的div
函数的Annotate:
如果你想要观察其他event ( 预设cycles ) 和指定取样频率( 预设每秒4000次) :
1 | perf top -e cache-misses -c 4000 |
perf stat
该命令用在已知哪个程序需要优化,使用该命令对程序进行时间采样分析,找到可以优化的地方。如对perf_tes.c
进行stat分析:
1 | [root@k8s-master ~] |
不加-e
选项指定你所感兴趣的特殊事件,perf stat将采集如下这些事件:
-
Task-clock-msecs:CPU 利用率,该值高,说明程序的多数时间花费在 CPU 计算上而非 IO。
-
Context-switches:进程切换次数,记录了程序运行过程中发生了多少次进程切换,频繁的进程切换是应该避免的。
-
Cache-misses:程序运行过程中总体的 cache 利用情况,如果该值过高,说明程序的 cache 利用不好
-
CPU-migrations:表示进程 t1 运行过程中发生了多少次 CPU 迁移,即被调度器从一个 CPU 转移到另外一个 CPU 上运行。
-
Cycles:处理器时钟,一条机器指令可能需要多个 cycles,
-
Instructions: 机器指令数目。
-
IPC:是 Instructions/Cycles 的比值,该值越大越好,说明程序充分利用了处理器的特性。
-
Cache-references: cache 命中的次数
-
Cache-misses: cache 失效的次数。
perf record / perf report
使用 top 和 stat 之后,您可能已经大致有数了。要进一步分析,便需要一些粒度更细的信息。比如说您已经断定目标程序计算量较大,也许是因为有些代码写的不够精简。那么面对长长的代码文件,究竟哪几行代码需要进一步修改呢?这便需要使用 perf record 记录单个函数级别的统计信息,并使用 perf report 来显示统计结果。
调优应该将注意力集中到百分比高的热点代码片段上,假如一段代码只占用整个程序运行时间的 0.1%,即使您将其优化到仅剩一条机器指令,恐怕也只能将整体的程序性能提高 0.1%。俗话说,好钢用在刀刃上。
使用record的时候,可能会出现的问题是取样频率太低,有些函数的信息没有没取样到而无法显示出来,可以通过 -F
选项调整取样频率,最大频率值为cat /proc/sys/kernel/perf_event_max_sample_rate
。
Perf 调优举例
上面通过死循环的代码,对常见的perf命令进行介绍,发现线上服务器的热点事件。下面将通过另一个例子,使用perf发现程序的问题并进行优化。
程序2,cpu_cache_miss.c
:
1 | static char array[10000][10000]; |
1 | perf stat --repeat 5 -e cache-misses,cache-references,instructions,cycles ./cpu_cache_miss |
如果我们善用一下存取的局部性,将i,j
对调改成array[i][j]++
,可参考与程序员相关的CPU缓存知识,优化后:
1 | perf stat --repeat 5 -e cache-misses,cache-references,instructions,cycles ./cpu_cache_miss_new |
可见IPC从0.83上升到了1.36。
火焰图
生成火焰图的步骤可以参考之前的总结文章:CPU火焰图
参考文章和扩展阅读
- PERF tutorial: Finding execution hot spots
- 与程序员相关的CPU缓存知识
- Cache的基本原理
- Linux 效能分析工具: Perf
- Perf – Linux下的系统性能调优工具,第 1 部分
- CPU火焰图
赞赏支持一下