火焰图解析

火焰图解析

火焰图(flame graph)是性能分析的利器,是基于 perf 结果产生的 SVG 图片,用来展示 CPU 的调用栈。

火焰图解析

  • y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。
  • x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。

火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调。

互动性

火焰图是 SVG 图片,可以与用户互动。

(1)鼠标悬浮

火焰的每一层都会标注函数名,鼠标悬浮时会显示完整的函数名、抽样抽中的次数、占据总抽样次数的百分比。下面是一个例子。

mysqld'JOIN::exec (272,959 samples, 78.34 percent)

(2)点击放大

在某一层点击,火焰图会水平放大,该层会占据所有宽度,显示详细信息。

火焰图

左上角会同时显示"Reset Zoom",点击该链接,图片就会恢复原样。

(3)搜索

按下 Ctrl + F 会显示一个搜索框,用户可以输入关键词或正则表达式,所有符合条件的函数名会高亮显示。

火焰图示例

下面是一个简化的火焰图例子。首先,CPU 抽样得到了三个调用栈。

func_c
func_b
func_a
start_thread

func_d
func_a
start_thread

func_d
func_a
start_thread

上面代码中,start_thread 是启动线程,调用了 func_a。后者又调用了 func_b 和 func_d,而 func_b 又调用了 func_c。经过合并处理后,得到了下面的结果,即存在两个调用栈,第一个调用栈抽中 1 次,第二个抽中 2 次。

start_thread;func_a;func_b;func_c 1
start_thread;func_a;func_d 2

有了这个调用栈统计,火焰图工具就能生成 SVG 图片。

SVG 图片

上面图片中,最顶层的函数 g()占用 CPU 时间最多。d()的宽度最大,但是它直接耗用 CPU 的部分很少。b()和 c()没有直接消耗 CPU。因此,如果要调查性能问题,首先应该调查 g(),其次是 i()。另外,从图中可知 a()有两个分支 b()和 h(),这表明 a()里面可能有一个条件语句,而 b()分支消耗的 CPU 大大高于 h()。

局限

两种情况下,无法画出火焰图,需要修正系统行为。

(1)调用栈不完整

当调用栈过深时,某些系统只返回前面的一部分(比如前 10 层)。

(2)函数名缺失

有些函数没有名字,编译器只用内存地址来表示(比如匿名函数)。

浏览器的火焰图

Chrome 浏览器可以生成页面脚本的火焰图,用来进行 CPU 分析。打开开发者工具,切换到 Performance 面板。然后,点击"录制"按钮,开始记录数据。这时,可以在页面进行各种操作,然后停止"录制"。这时,开发者工具会显示一个时间轴。它的下方就是火焰图。

浏览器的火焰图

浏览器的火焰图与标准火焰图有两点差异:它是倒置的(即调用栈最顶端的函数在最下方);x 轴是时间轴,而不是抽样次数。

浏览器的火焰图