perf record是采集C++程序CPU性能数据的常用工具,需编译时加-g和-fno-omit-frame-pointer以保留调试符号和帧指针,执行perf record -F 99 -g -- ./your_program采样,再经perf script和stackcollapse-perf.pl转换为折叠格式,最后用flamegraph.pl生成火焰图。

用perf record采集C++程序的CPU性能数据
FlameGraph本身不采集数据,它只是把已有的栈采样数据转成可视化火焰图。对C++程序来说,最常用、开销低、精度够的采集工具是Linux自带的perf。关键前提是你的二进制必须带调试符号(即编译时加-g),否则函数名会显示为[unknown]或十六进制地址。
执行以下命令运行并采样:
perf record -F 99 -g -- ./your_program arg1 arg2
注意几个易错点:
-
-F 99表示每秒采样99次,太高(如1000)会导致显著性能干扰,太低(如10)可能漏掉短生命周期热点 -
-g启用调用图(call graph),这是生成火焰图的必要条件;若用--call-graph dwarf可提升内联函数识别率(需编译时加-fno-omit-frame-pointer) - 确保
your_program是未strip的可执行文件;如果用CMake,确认CMAKE_BUILD_TYPE不是Release(或手动加-g) - 采样期间程序必须实际跑起来——
perf record不会自动启动程序,别忘了最后的./your_program
把perf.data转成折叠格式(folded stack traces)
FlameGraph脚本只认特定文本格式:每一行是一条“折叠栈”,形如main;foo;bar;baz 123,末尾数字是该栈出现次数。这一步不能跳过,也不能手写。
立即学习“C++免费学习笔记(深入)”;
用perf script导出原始栈,再通过stackcollapse-perf.pl转换:
perf script | ~/FlameGraph/stackcollapse-perf.pl > out.perf-folded
常见失败原因:
-
stackcollapse-perf.pl找不到:必须从Brendan Gregg官方仓库下载整个FlameGraph目录,不要只取单个脚本 - 输出为空:大概率是
perf record没采到有效样本,检查是否程序立即退出、是否权限不足(需sudo或配置/proc/sys/kernel/perf_event_paranoid)、是否用了容器导致perf受限 - 函数名全是
__libc_start_main或地址:说明缺少debug info,重新编译加-g -fno-omit-frame-pointer
用flamegraph.pl生成SVG火焰图
有了out.perf-folded,就只剩最后一步:
~/FlameGraph/flamegraph.pl out.perf-folded > flame.svg
生成的flame.svg可直接用浏览器打开。注意:
- 默认颜色无业务含义,只区分调用层级;宽度代表该函数在采样中出现的相对时间比例
- 点击函数框可折叠/展开其子树,右键可复制函数名——这对定位模板实例化爆炸(如
std::vector<:map>></:map>)特别有用 - 若图中出现大量
pthread_cond_wait或epoll_wait,说明程序大部分时间在等IO或锁,此时应切换到perf record -e cycles,instructions,cache-misses做事件分析,而非只看CPU - 不要用
--title等参数过度定制;SVG本身不支持字体嵌入,换机器打开可能文字错位
C++特有问题:模板和内联让火焰图变宽变浅
现代C++大量使用模板和inline,会导致火焰图里同一逻辑分散成几十个相似函数名(如std::vector<int>::push_back</int>、std::vector<double>::push_back</double>),横向拉得很宽,掩盖真正瓶颈。
缓解方法有限但实用:
- 用
sed预处理折叠文件,合并泛化名称:sed 's/::[a-zA-Z0-9_]*<[^>]*>/::<T>/g' out.perf-folded | ~/FlameGraph/flamegraph.pl > flame-generic.svg
- 编译时加
-frecord-gcc-switches,部分版本gcc会在debug info里记录模板实例化来源,perf script能更好映射 - 对关键路径,临时加
__attribute__((noinline))强制不内联,让热点更集中——这不是长期方案,但排查时很管用
火焰图不是银弹。它擅长暴露“哪个函数耗CPU最多”,但没法告诉你“为什么这个函数慢”;看到std::sort占30%,得接着看传入的比较器是否写了O(n)逻辑,或者容器是否未预留空间导致频繁重分配。这些只能回代码里查。


















