0%

Perf 使用手册

  • 在追求极致性能的场景下,了解你的程序运行过程中 CPU 在做什么是非常重要的,火焰图就是一种非常直观展示 CPU 在程序整个生命周期过程中时间分配的工具。
  • 在进行程序性能分析时,通常会使用 perf 工具来采集程序运行时的性能数据,然后通过 Flame Graph 将 perf 采集到的数据转换成火焰图进行展示。
  • 本文主要介绍 C++ 开发者常用的工具集的底层原理、应用场景以及相关流程和使用方法。

如何生成火焰图

安装 perf 工具

1
2
sudo apt-get install linux-tools-common linux-tools-$(uname -r)
perf --version

perf 是 Linux 内核性能分析工具,用于采集程序运行时的性能数据,可以用于分析 CPU 性能、内存性能、I/O 性能等方面的性能问题,linux-tools-common 包含了 perf 工具的通用组件。

安装 FlameGraph 工具

1
git clone https://github.com/brendangregg/FlameGraph.git

FlameGraph 用于将 perf 采集到的数据转换成火焰图。

更改 perf 配置

1
2
3
sudo vim /etc/sysctl.conf  # 添加 kernel.perf_event_paranoid = -1
sudo sysctl -p
cat /proc/sys/kernel/perf_event_paranoid

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
2
3
sudo perf script > out.perf
./stackcollapse-perf.pl out.perf > out.folded
./flamegraph.pl out.folded > flamegraph.svg

perf script 将 perf 采集到的数据转换成文本格式,stackcollapse-perf.pl 将文本格式的数据转换成折叠堆栈,flamegraph.pl 将折叠格式的数据生成火焰图 SVG 文件。

查看火焰图

1
xdg-open flamegraph.svg

当用浏览器打开生成的 flamegraph.svg 文件时,会看到类似下图的火焰图:
图1

如何分析火焰图

火焰图是对性能采样数据的堆栈轨迹进行可视化处理后的产物。以图1为例,是下面这段 C++ 代码的火焰图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <vector>
#include <cmath>

void heavy_computation() {
double val = 0;
for (int i = 0; i < 100000000; ++i) {
val += std::sin(i) * std::cos(i);
}
std::cout << "Heavy Task Done: " << val << std::endl;
}

void light_task() {
double val = 0;
for (int i = 0; i < 10000000; ++i) {
val += std::sqrt(i);
}
std::cout << "Light Task Done: " << val << std::endl;
}

int main() {
while (true) {
heavy_computation();
light_task();
}
return 0;
}

以下是理解火焰图的几个基本维度。

横轴与纵轴

  • 横轴表示 CPU 时间的分布,越宽的部分表示该函数在所有采样中占用的 CPU 时间越多。
  • 纵轴表示调用关系,从下往上看,每一层代表一个调用层级,越高的部分表示调用层级越深。

颜色与交互

  • 火焰图的颜色通常是随机生成的,以暖色调为主,仅用于区分邻近的函数名。颜色深浅与性能好坏无关。
  • 在浏览器中查看火焰图时,可以通过鼠标悬停查看每个函数的详细信息,包括函数名、占用的 CPU 时间百分比等。
  • 点击某个函数可以查看该函数内部的调用关系,进一步分析性能瓶颈。
  • 点击右上角的 “Search” 输入框可以搜索特定函数,可以快速定位到感兴趣的函数的位置。

关键图形特征

在实际分析时,需要寻找以下三种特征进行性能瓶颈的定位与优化:

  • 特征 A:平顶 (Plateaus)
    • 形态:方块顶部很平,且上方没有更细的子调用。
    • 解读:这说明 CPU 确实卡在这个函数内部进行计算。
    • 对策:优化该函数的算法或减少其内部指令开销。

  • 特征 B:宽大的支柱 (Wide Pillars)
    • 形态:从底部一直延伸到顶部的宽大条柱。
    • 解读:这代表一个完整的调用流程占用了大量的总时间。
    • 对策:如果顶层函数已经很难优化,考虑在底层减少对该流程的调用频率。

  • 特征 C:尖顶 (Spikes)
    • 形态:细而高的突起。
    • 解读:这通常代表深度递归或非常零碎的短时间调用。
    • 对策:通常不是性能瓶颈,除非这种尖顶密密麻麻地出现。

参考资料