3、perf工具入门:perf list、perf stat、perf record、perf report的基本使用
perf 这玩意儿,说白了就是 Linux 系统自带的性能剖析神器。我刚开始接触它的时候,觉得命令行参数又多又杂,但用顺手之后,真香。今天我就带你把这四个最核心的子命令捋一遍:perf list、perf stat、perf record 和 perf report。
你想想看,一个程序跑得慢,你总得知道它慢在哪吧?是 CPU 太忙?还是内存访问太慢?或者是锁竞争太激烈?perf 就是帮你回答这些问题的。
3.1 perf list:看看你能监控什么
我第一次用 perf 时,第一件事就是敲 perf list。它会列出当前系统支持的所有性能事件。硬件事件、软件事件、tracepoint 事件,全给你列出来。
# 列出所有事件
perf list
# 只看硬件事件
perf list hw
# 只看软件事件
perf list sw
# 只看 cache 相关事件
perf list cache
我个人习惯先跑一遍 perf list | grep -i "cache",看看我的 CPU 支持哪些缓存事件。不同架构的 CPU,支持的事件差别挺大的。比如 Intel 和 AMD 的 LLC-loads 事件名就不一样。
perf list 配合 grep 模糊搜索。比如 perf list | grep branch 就能找到所有分支预测相关的事件。
3.2 perf stat:快速统计,一眼看穿
perf stat 是我用得最多的命令。它就像程序的体检报告,跑完一次程序,直接告诉你各种关键指标。我曾经用它排查过一个线上服务 CPU 使用率异常的问题,一跑 stat 就发现 context-switch 高得离谱,原来是线程数开太多了。
# 统计 ls 命令的性能数据
perf stat ls
# 统计一个正在运行的进程(PID 为 12345)
perf stat -p 12345
# 统计并显示更详细的事件
perf stat -e cycles,instructions,cache-misses,cache-references ./myapp
输出大概长这样:
Performance counter stats for 'ls':
1.234.567 cycles # 0.123 GHz
987.654 instructions # 0.80 insn per cycle
1.234 cache-misses # 5.67% of all cache refs
21.765 cache-references
0.001234567 seconds time elapsed
这里有个关键指标叫 IPC(Instructions Per Cycle)。IPC 越高,说明 CPU 执行效率越高。如果 IPC 低于 0.5,那基本可以断定你的程序在频繁等待内存或者有严重的流水线停顿。
-r N 参数,比如 perf stat -r 5 ./myapp 会跑 5 次并给出平均值和标准差。
3.3 perf record:记录现场,秋后算账
perf stat 只能看个总数,但如果你想深入分析,比如知道某个函数到底消耗了多少 CPU 时间,那就得上 perf record。它会采样程序的运行状态,生成一个 perf.data 文件。
# 采样 myapp 程序,默认以 1000Hz 的频率采样
perf record ./myapp
# 采样正在运行的进程
perf record -p 12345
# 指定采样频率(4000Hz),频率越高越精确,但开销也越大
perf record -F 4000 ./myapp
# 只采样 CPU 周期事件
perf record -e cycles ./myapp
嗯,这里要注意:采样频率不是越高越好。我曾经在某个项目里把频率设到 10000Hz,结果 perf 本身的开销就把程序拖慢了 20%,数据反而失真了。一般来说,1000Hz 到 4000Hz 是个比较合理的范围。
-e cache-misses。
3.4 perf report:数据解读,找到真凶
采样完了,怎么分析?perf report 就是干这个的。它会读取 perf.data,然后按函数维度展示 CPU 时间占比。
# 查看报告
perf report
# 以调用链的形式展示
perf report --call-graph
# 按百分比排序,只看占比超过 1% 的函数
perf report --percent-limit 1
输出界面是交互式的,你可以用上下键选择函数,按回车展开调用链。我最常用的操作是:
- 先看最上面几个函数,它们就是热点。
- 按
a键注释,可以给函数加备注。 - 按
Enter展开调用链,看看这个函数是被谁调用的。
我曾经遇到过一个诡异的性能问题:某个函数占了 30% 的 CPU,但代码逻辑很简单,就是几个加法。展开调用链才发现,它被一个循环调用了上亿次,而且每次调用都触发了一次内存分配。说白了,问题不在函数本身,而在调用频率和内存分配的开销上。
3.5 一张图看懂 perf 工作流程
下面这张 SVG 图,帮你把 perf 的四个命令串起来理解:
3.6 实战:一个完整的分析流程
光说不练假把式。我们拿一个简单的 C 程序来演示:
// test.c
#include <stdio.h>
#include <stdlib.h>
void hot_function() {
int sum = 0;
for (int i = 0; i < 100000000; i++) {
sum += i;
}
printf("sum = %d\n", sum);
}
int main() {
hot_function();
return 0;
}
编译并分析:
# 编译
gcc -O0 -g -o test test.c
# 第一步:快速统计
perf stat ./test
# 第二步:采样
perf record ./test
# 第三步:分析报告
perf report
在 perf report 界面里,你会看到 hot_function 占了几乎 100% 的 CPU 时间。这就是热点。如果你用 --call-graph 参数,还能看到 main 调用了 hot_function 的完整调用链。
-fno-omit-frame-pointer 参数。加上这个参数后,调用链就完整了。记住:编译时加 -g -fno-omit-frame-pointer,否则你看到的调用链可能残缺不全。
3.7 总结一下
perf 这四个命令,你记住一个口诀就行:
- perf list:看菜单,有什么菜(事件)可以点。
- perf stat:快速结账,看总开销。
- perf record:录像,把现场录下来。
- perf report:回放录像,逐帧分析。
说白了,perf 就是你的性能放大镜。先用 stat 看全局,再用 record + report 看细节。别一上来就 record,那样数据量太大,反而抓不住重点。
想要更多资料,联系可可