使用火焰图分析cpu

82 阅读2分钟

1. 火焰图的概念

以下是比较官方的解释:

一种软件执行情况的形象化的反映,用于做性能profile和debug。

在Flame Graph在呈现出整个最终结果之前,其中主要的工作并不是图形的展现,而是前面profile数据的采集。 这里数据的采集首先按照profile的类型分为以下几类:

  • (1)CPU的profile分析,那需要进行的stack trace的采集,可以借助于Linux的perf命令
  • (2)Off-CPU的profile,那些没有消耗CPU的线程的profile,可以理解为被block住的线程。 以上不同类型的profile需要依赖不同的采集工具,对于CPU的profile,它要做的其实就是进程stack trace的收集。这个收集可以按照采集总时间,采集频率来进行。

2. CPU的profile分析

我们要分析的是某个任务在cpu被执行的过程中,具体的调用栈长啥样。所以我们关注的是这个任务在运行态时,执行它的cpu的栈长啥样。


image

因此,明确当此任务不在cpu上的时候(处于就绪态或者阻塞态),该cpu采样的结果不统计。 执行:

perf record -F 99 -a -g -p $pid -- sleep 1

就表示在所有cpu上,在1s的时间窗口内,每1/99s的时间间隔采样。如下图,示例了一个cpu上采样的过程。


![image](uploads/b46aefacfe81e62365955a1caaf1dd57/image.png)

然后收集当前指令地址(程序计数器)或整个堆栈信息——得到99个快照,我们把这99个快照聚合,Off-CPU阶段,perf采集不到cpu上这个任务的任何信息,所以下图参与聚合的快照,可能只有80张。这样我们就得到了该cpu上一个包含统计信息的数据。


![image](uploads/8ea1d0fd4646f63d10146a1b9286c9b2/image.png)

实际上的多CPU得到的统计结果是很乱的,生成的stack trace全部展现出来会相当的庞大。虽然perf命令可以帮助对此进行折叠,汇总,但是仍然不便于我们分析。所以火焰图就是我们最后一步使用的聚合工具。展现效果如下图中所示。


![image](uploads/80811cc5840bf3ccc719e6f48b0add19/image.png)

上述图中,y轴高度表示的stack trace的调用深度,从上往下为从child–>parent的关系,x轴宽度表示调用的频率,所以我们在观察flame Graph的时候,要观察最顶部的那些宽度比较宽的stack信息。Flame Graph通过图形的方式能够帮助开发者迅速定位出明显消耗资源的调用

3. 一个案例:On-cpu火焰图分析fm_item性能

离线时间从413s(80c)优化到180s(80c)以内。时间显著降低,在进程的cpu利用率已经非常高的情况下满足了业务同学对fm_item物料向量生成时间降低的需求(业务同学在32c机器运行>600s,64c 460s)。以下是fm_item的流程简图


![image](uploads/3db5355f861e0feec48f806c1d1f8988/image.png)

首次测试,以上流程各阶段耗时分析:


![image](uploads/a6e282694dc4e155e468a3cbdaa2df76/image.png)

换80c的机器测试main_ps_editons并行数80, 耗时 t = 413ms,此处top的数据未记录,cpu利用率已经在90%以上。

针对这种机器cpu几乎打满,机器负载很高的情况,我们再做并行的优化意义不大,所以考虑用火焰图对fm_item进程进行On-cpu分析,看看究竟是哪些函数耗尽了cpu,然后看看这些函数有没有机会优化来减少计算或查找。采集的数据通过火焰图呈现如下:


![image](uploads/60be473b6c8a7a4d1651c7bff9eefb1c/image.png)

接下来就是对compute_mid_w2进行优化,这里是因为多余的map search次数高达75%。优化后线程数 = 80不变,耗时 t = 156ms, 物料向量生成时常降低了62%。可见结合火焰图分析进程占用cpu的情况,可以有效搜集到关键函数,从而给进一步优化指明方向。


![image](uploads/871d8b7acd972aa0369491a8977cf24f/image.png)

4. cpu利用率太高的一些优化措施

  • (1)减少多余的操作,例如案例里的map search,json obj里的findMember代替hasMember,obj[member_name]的方式。
  • (2)减少数据的深拷贝。
  • (3)局部性原理,对数组按行进行访问时,具有更好的空间局部性,Cache命中率更高。

5. On-cpu火焰图分析的简要步骤:

官网资料:www.brendangregg.com/FlameGraphs…

  • (1)准备工作:服务器上下载好包FlameGraph.tar.gz并解压 wget minio.eos.grid.sina.com.cn:9100/software/Fl…

  • (2)先启动进程,再启动perf记录,sleep后的参数是指perf采集的时间 99表示的采样频率,pid就是我们要测量的进程id perf record -F 99 -a -g -p $pid -- sleep 60

  • (3)perf 进程结束后,数据记录在perf.data中,如下图:

![image](uploads/c48421158d42f2bbd3225af06f21bb8b/image.png)
  • (4)接下来执行生成out.perf-folded中间文件 perf script | ./FlameGraph/stackcollapse-perf.pl > out.perf-folded

  • (5)接着继续执行,把out.perf-folded转换成perf-kernel.svg文件: ./FlameGraph/flamegraph.pl out.perf-folded > perf-kernel.svg

  • (6)启动一个web server 比如运行python -m SimpleHTTPServer 8080

出现如下页面,点击红色箭头指向的perf-kernel.svg即可显示火焰图:


![image](uploads/947a8f60a6ae75f95de33eb633add19a/image.png)

最后就得到长这个样子的火焰图了,接下来的目标就是看看那些调用栈大平顶对应的函数,找到优化的方法。


![image](uploads/7ed858f25d0347b26ef81d54eeab30cb/image.png)