- 在追求极致性能的场景下,了解你的程序运行过程中 CPU 在做什么是非常重要的,火焰图就是一种非常直观展示 CPU 在程序整个生命周期过程中时间分配的工具。
- 在进行程序性能分析时,通常会使用 perf 工具来采集程序运行时的性能数据,然后通过 Flame Graph 将 perf 采集到的数据转换成火焰图进行展示。
- 本文主要介绍 C++ 开发者常用的工具集的底层原理、应用场景以及相关流程和使用方法。
如何生成火焰图
安装 perf 工具
1 | sudo apt-get install linux-tools-common linux-tools-$(uname -r) |
perf 是 Linux 内核性能分析工具,用于采集程序运行时的性能数据,可以用于分析 CPU 性能、内存性能、I/O 性能等方面的性能问题,
linux-tools-common包含了 perf 工具的通用组件。
安装 FlameGraph 工具
1 | git clone https://github.com/brendangregg/FlameGraph.git |
FlameGraph 用于将 perf 采集到的数据转换成火焰图。
更改 perf 配置
1 | sudo vim /etc/sysctl.conf # 添加 kernel.perf_event_paranoid = -1 |
kernel.perf_event_paranoid的值为 -1 表示允许所有用户访问 perf 事件。
采集性能数据
1 | sudo perf record -F 99 -p <pid> -g -- sleep 30 |
-F 99表示采样频率为 99Hz,-p <pid>表示要分析的进程 ID,-g表示采集调用栈信息,-- sleep 30表示采样持续时间为 30 秒。
生成火焰图
1 | sudo perf script > out.perf |
perf script将 perf 采集到的数据转换成文本格式,stackcollapse-perf.pl将文本格式的数据转换成折叠堆栈,flamegraph.pl将折叠格式的数据生成火焰图 SVG 文件。
查看火焰图
1 | xdg-open flamegraph.svg |
当用浏览器打开生成的 flamegraph.svg 文件时,会看到类似下图的火焰图:
如何分析火焰图
火焰图是对性能采样数据的堆栈轨迹进行可视化处理后的产物。以图1为例,是下面这段 C++ 代码的火焰图:
1 |
|
以下是理解火焰图的几个基本维度。
横轴与纵轴
- 横轴表示 CPU 时间的分布,越宽的部分表示该函数在所有采样中占用的 CPU 时间越多。
- 纵轴表示调用关系,从下往上看,每一层代表一个调用层级,越高的部分表示调用层级越深。
颜色与交互
- 火焰图的颜色通常是随机生成的,以暖色调为主,仅用于区分邻近的函数名。颜色深浅与性能好坏无关。
- 在浏览器中查看火焰图时,可以通过鼠标悬停查看每个函数的详细信息,包括函数名、占用的 CPU 时间百分比等。
- 点击某个函数可以查看该函数内部的调用关系,进一步分析性能瓶颈。
- 点击右上角的 “Search” 输入框可以搜索特定函数,可以快速定位到感兴趣的函数的位置。
关键图形特征
在实际分析时,需要寻找以下三种特征进行性能瓶颈的定位与优化:
- 特征 A:平顶 (Plateaus)
- 形态:方块顶部很平,且上方没有更细的子调用。
- 解读:这说明 CPU 确实卡在这个函数内部进行计算。
- 对策:优化该函数的算法或减少其内部指令开销。
- 特征 B:宽大的支柱 (Wide Pillars)
- 形态:从底部一直延伸到顶部的宽大条柱。
- 解读:这代表一个完整的调用流程占用了大量的总时间。
- 对策:如果顶层函数已经很难优化,考虑在底层减少对该流程的调用频率。
- 特征 C:尖顶 (Spikes)
- 形态:细而高的突起。
- 解读:这通常代表深度递归或非常零碎的短时间调用。
- 对策:通常不是性能瓶颈,除非这种尖顶密密麻麻地出现。