3、perf工具入门:perf list、perf stat、perf record、perf report的基本使用

perf 这玩意儿,说白了就是 Linux 系统自带的性能剖析神器。我刚开始接触它的时候,觉得命令行参数又多又杂,但用顺手之后,真香。今天我就带你把这四个最核心的子命令捋一遍:perf list、perf stat、perf record 和 perf report。

你想想看,一个程序跑得慢,你总得知道它慢在哪吧?是 CPU 太忙?还是内存访问太慢?或者是锁竞争太激烈?perf 就是帮你回答这些问题的。

核心思想: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,那基本可以断定你的程序在频繁等待内存或者有严重的流水线停顿。

注意:perf stat 默认只统计一次。如果你需要多次统计取平均值,可以用 -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 是个比较合理的范围。

建议:如果你只想分析热点函数,用默认的 cycles 事件就够了。如果你想分析 cache 失效,可以指定 -e cache-misses。

3.4 perf report:数据解读,找到真凶

采样完了,怎么分析?perf report 就是干这个的。它会读取 perf.data,然后按函数维度展示 CPU 时间占比。

# 查看报告
perf report

# 以调用链的形式展示
perf report --call-graph

# 按百分比排序,只看占比超过 1% 的函数
perf report --percent-limit 1

输出界面是交互式的,你可以用上下键选择函数,按回车展开调用链。我最常用的操作是:

  1. 先看最上面几个函数,它们就是热点。
  2. 按 a 键注释,可以给函数加备注。
  3. 按 Enter 展开调用链,看看这个函数是被谁调用的。

我曾经遇到过一个诡异的性能问题:某个函数占了 30% 的 CPU,但代码逻辑很简单,就是几个加法。展开调用链才发现,它被一个循环调用了上亿次,而且每次调用都触发了一次内存分配。说白了,问题不在函数本身,而在调用频率和内存分配的开销上。

关键点:perf report 展示的是 采样占比,不是精确的指令计数。采样次数越多,统计结果越接近真实值。如果某个函数占比 5% 以上,基本可以确定它是热点。

3.5 一张图看懂 perf 工作流程

下面这张 SVG 图,帮你把 perf 的四个命令串起来理解:

perf 工具工作流程 perf list 查看可用事件 perf stat 快速统计总数 perf record 采样生成数据 perf.data 采样数据文件 perf report 分析热点函数 先看有什么事件 再快速统计 最后采样分析

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 的完整调用链。

避坑指南:我曾经在分析一个多线程程序时,发现 perf report 显示的调用链不全。后来才发现,编译时忘了加 -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,那样数据量太大,反而抓不住重点。

想要更多资料,联系可可