3. 性能分析工具入门:perf、FlameGraph、pprof 的使用方法及火焰图解读
做性能调优,工具是第一步。
我个人习惯,先跑一遍 perf 看看全局,再用 FlameGraph 可视化热点,最后用 pprof 深挖具体函数。三件套用熟了,CPU 被谁吃了,一目了然。
这一章,咱们就过一遍它们的核心用法。不废话,直接上手。
3.1 perf:Linux 原生性能剖析器
perf 是 Linux 内核自带的工具。说白了,它就是个采样器。以固定频率打断 CPU,记录当前在执行什么函数。采样次数多了,热点函数就暴露出来了。
我遇到过好几次,业务方说「我的模块 CPU 占用高」,结果我一跑 perf,发现 70% 的时间都在内核态做内存分配。嗯,这时候就该想想怎么减少频繁的 malloc/free 了。
常用命令
# 记录整个系统的 CPU 采样,持续 10 秒
sudo perf record -a -g -- sleep 10
# 只看某个进程
sudo perf record -p <PID> -g -- sleep 10
# 生成报告
sudo perf report -g
| 参数 | 含义 | 我的建议 |
|---|---|---|
-a |
全系统采样 | 排查共享资源竞争时很管用 |
-g |
记录调用栈 | 必加!没有栈信息,火焰图就画不出来 |
-p |
指定目标进程 | 调优 SDK 时最常用 |
-- |
分隔符 | 后面跟采样时长或额外命令 |
我曾经踩过一个坑:perf record 没加 -g,结果采了半天没有调用栈信息,火焰图全是「unknown」。白干了。所以每次跑之前,确认一下参数。
3.2 FlameGraph:让热点一目了然
perf 生成的报告是文本的,看起来费劲。Brendan Gregg 大神写的 FlameGraph 脚本,能把 perf 数据变成 火焰图。
为什么叫火焰图?因为颜色从黄到红,表示代码越来越「热」。你想想看,一堆矩形堆叠在一起,越宽的方块代表 CPU 耗时越多。一眼扫过去,哪个函数最宽,谁就是元凶。
生成火焰图
# 1. 把 perf 采样数据转换为 FlameGraph 的格式
perf script -i perf.data > out.perf
# 2. 折叠调用栈
./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
# 3. 生成 SVG 火焰图
./FlameGraph/flamegraph.pl out.folded > flame.svg
- 顶部:当前正在执行的函数
- 底部:入口函数(通常是 main 或启动线程的函数)
- 宽度:该函数在采样中出现的比例
- 颜色:暖色(红/橙)表示频繁被采样到
我一般先看「山顶」有没有特别宽的红色方块。如果有,十有八九是热点。
3.3 pprof:Go 与 C/C++ 的深度分析利器
如果你调优的对象是 Go 写的 SDK,或者编译时带了 gperftools,那 pprof 是绕不开的。它能帮你分析 CPU、内存、阻塞、锁竞争,非常全面。
曾经有个线上问题,CPU 跑满了,用 perf 看到一个大热点,但搞不清是谁调了它。我用 pprof 的 top 和 peek 命令一查,发现是一个第三方库在无限循环里做日志格式化。嗯,最后直接把日志级别调高,问题秒解。
常用命令
# 查看 CPU 热点
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile
# 查看内存分配
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/heap
# 对于 C/C++,使用 gperftools
CPUPROFILE=/tmp/profile.prof ./your_app
pprof --text your_app /tmp/profile.prof
| 工具 | 适用语言 | 数据源 |
|---|---|---|
| pprof | Go、C/C++ (gperftools) | 内置 /debug/pprof 端点 或 分析文件 |
| perf | 全语言 (基于采样) | 内核 PMU、软件事件 |
| FlameGraph | 通用 (可视化层) | perf / pprof 的输出 |
3.4 工具链协同流程
这三者不是孤立的。我通常这样串联:
- 先用 perf 扫全局:确认热点在内核态还是用户态,是 CPU bound 还是锁竞争。
- 再用 FlameGraph 可视化:生成火焰图,肉眼定位宽函数。
- 最后用 pprof 深挖:进入函数内部,看调用链和堆栈细节。
一句话总结:perf 是听诊器,FlameGraph 是 X 光片,pprof 是显微镜。
3.5 火焰图深度解读
拿到火焰图,别急着说「这里宽」。要看三样东西:
- 纵轴深度:调用链有多长。层数太深说明有过度抽象或代理模式滥用。
- 横轴宽度:具体函数的占比。一个函数的宽度超过 20%,基本就是首要优化点。
- 颜色分布:如果火苗是「平顶」的——也就是顶部有很多宽度接近的方块——说明代码在多个热点之间频繁切换,可能是调度开销或频繁锁切换。
我曾经看到过一张火焰图,顶层是一个很小的函数,但底层是一大片微小的方块。我一开始以为是它自身开销大,后来仔细看,底下全是内存拷贝。说白了,问题不在顶层函数,在底层的 memcpy。所以看火焰图,要自上而下穿透。
3.6 小结与实战建议
- perf:第一道关卡,搞清楚整体画像。
- FlameGraph:把数字变成图形,直觉上更容易发现「异常形状」。
- pprof:定点深挖,看调用关系和内联优化。
每次调优前,先保存一份「优化前」的火焰图。改动代码后,再生成一份对比。两个图叠在一起看,哪里变宽了,哪里消失了,非常直观。我习惯把这个对比图直接贴到代码评审里,比解释一百句话都管用。