广告

C++在Linux上用Perf工具进行性能分析,并绘制与解读火焰图的完整教程

一、准备工作与环境

要在 Linux 上对 C++ 项目进行性能分析,首要任务是搭建完整的工具链。本文将详细介绍如何使用 Perf 工具进行采样、如何生成 火焰图(Flame Graph)并对结果进行解读。通过本教程,你将理解整个分析链路从数据采集到可视化解读的完整过程。

在实际环境中,确保内核符号和调试信息可用,这有助于 perf 产出可读的调用图与函数名映射。调试信息 的存在通常来自于编译时开启的调试选项以及系统中可用的内核符号。

C++在Linux上用Perf工具进行性能分析,并绘制与解读火焰图的完整教程

安装 perf 工具

不同发行版安装方式略有差异,以下给出常用方案,确保你能够顺利开始性能分析。

# Debian/Ubuntu
sudo apt-get update
sudo apt-get install linux-tools-common linux-tools-$(uname -r) perf# RHEL/CentOS/Fedora
sudo yum install perf  # 或者在新版本中使用: sudo dnf install perf

安装完成后,可以用 perf --version 验证版本,并确认 perf 已就绪。如果你的目标是生成火焰图,额外需要准备 FlameGraph 脚本(Brendan Gregg 的官方仓库)或等效实现。

二、在 C++ 项目中使用 Perf 进行性能分析

编译并开启调试信息

要获得可读的调用栈,务必在编译阶段开启 -g,并尽量保持合理的优化等级以便分析,同时确保源代码与生成的符号一致。调试信息有助于把热点映射回具体的源码位置。

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstdlib>void heavy_compute(size_t n) {volatile double sum = 0.0;for (size_t i = 0; i < n; ++i) {sum += std::sin(i) * std::cos(i);}
}int main() {std::vector<int> data(1000000);std::generate(data.begin(), data.end(), [](){ return rand(); });heavy_compute(1000000);std::cout << "done" << std::endl;return 0;
}

g++ 编译示例:

g++ -g -O2 main.cpp -o app

在实际运行中启动 perf 记录

为了获得调用图,需要结合执行的应用程序或对热区进行采样。常见做法是直接对可执行文件进行 perf record,或者对已运行中的进程进行采样。

# 直接对可执行文件进行 perf 记录
perf record -g ./app# 或对已运行的进程进行采样
pid=$(pidof app)
perf record -g -p $pid -- sleep 5

三、生成火焰图所需的数据与图形

将 perf 数据转换为火焰图所用的 folded 文件

火焰图需要将 perf 的调用栈转化为扁平化的格式,通常通过 perf script 输出,再借助 stackcollapse-perf.pl 脚本生成 folding 文件。

perf script > perf.script.out
# 或直接管道
perf script | ./stackcollapse-perf.pl > perf.folded

绘制火焰图的实际步骤

利用 FlameGraph 提供的绘制脚本,可以将 folding 文件转成 Svg 的火焰图。

# 假设你已经获得 perf.folded
./flamegraph.pl perf.folded > flamegraph.svg

注意事项:需要 perl 环境以及 FlameGraph 脚本,通常从 Brendan Gregg 的 FlameGraph 仓库获取。

四、在 FlameGraph 中解读火焰图

火焰图的结构要点

火焰图的水平宽度代表了时间开销的相对比例,越宽的区域表示该调用路径上的总耗时越多。横向面积的扩展越大,意味着该调用栈路径占用的总时间越多。

竖直方向的层级表示函数的调用关系,越靠上端的函数通常是调用方,越靠下的是被调用的函数。通过观察树状结构,可以快速定位“热路径”。

从火焰图读出优化方向

在合适的过滤条件下,最左侧的积木块往往对应热点函数,应该作为优先优化对象。记得关注 本地热点函数调用方/被调用方关系,以避免对非核心路径进行无效优化。

五、实战案例与完整流程回顾

案例背景

以一个简单的 C++ 计算密集型程序为例,目标是在 Linux 上通过 Perf 进行采样,并用 火焰图 直观呈现性能热点。

流程要点包括:确保代码包含调试信息、进行 perf record、再进行 perf script 与 flamegraph 的拼接绘制,最后对火焰图进行解读与定位优化点。

一个完整的命令串

以下示例覆盖完整流程:编译、采样、数据处理与图形生成。

# 1) 编译带调试信息
g++ -g -O2 main.cpp -o app# 2) 采样(开启调用栈)
perf record -g -F 99 ./app# 3) 生成折叠格式数据
perf script | ./stackcollapse-perf.pl > app.folded# 4) 生成火焰图
./flamegraph.pl app.folded > app_flamegraph.svg

解读要点

用浏览器打开 app_flamegraph.svg,在图中定位最宽的块。该区域通常对应最大的耗时函数以及它的直接或间接调用者。

通过对热点函数进行针对性优化,例如并行化、缓存友好实现、减少分配、减少锁竞争等,可以显著降低总耗时。

广告

后端开发标签