跳转至

perf

是什么 / 解决什么问题

perf 是 Linux 内核自带的性能分析工具,基于硬件性能计数器(PMU)和内核 tracepoint。开销极低,适合生产环境使用。可以回答"CPU 时间花在哪里"、"缓存命中率如何"、"哪些函数调用最频繁"等问题。

使用模式

perf stat — 统计概览

# 基本 CPU 统计
perf stat ./app
#  Performance counter stats for './app':
#       1,234.56 msec task-clock           # 0.95 CPUs utilized
#             12      context-switches
#              3      cpu-migrations
#          5,678      page-faults
#  4,567,890,123      cycles               # 3.7 GHz
#  3,456,789,012      instructions         # 0.76 IPC
#    234,567,890      branches
#     12,345,678      branch-misses        # 5.26%
#    567,890,123      cache-references
#     23,456,789      cache-misses         # 4.13%

# 关注特定事件
perf stat -e cache-misses,cache-references,L1-dcache-load-misses ./app

# 统计正在运行的进程
perf stat -p <pid> sleep 10

# 每秒输出一次(持续监控)
perf stat -I 1000 -p <pid>

关键指标解读: - IPC (Instructions Per Cycle):> 1 好,< 0.5 说明 CPU 在等待(内存/分支预测) - cache-misses / cache-references:> 5% 说明数据局部性差 - branch-misses:> 5% 说明分支预测差(可能有大量 if/switch)

perf record + report — 函数级热点

# 采样记录(默认采样 cycles 事件)
perf record -g ./app
# -g: 记录调用栈

# 对运行中进程采样 30 秒
perf record -g -p <pid> -- sleep 30

# 查看结果
perf report
# 交互界面显示函数级 CPU 占比
# + 号展开查看调用链

# 指定采样频率
perf record -F 99 -g -p <pid> -- sleep 30
# -F 99: 每秒 99 次采样(避免与系统时钟同步)

火焰图 (Flame Graph)

# 生成火焰图数据
perf record -F 99 -g -p <pid> -- sleep 30
perf script > perf.data.txt

# 使用 Brendan Gregg 的工具生成 SVG
git clone https://github.com/brendangregg/FlameGraph
./FlameGraph/stackcollapse-perf.pl perf.data.txt | \
    ./FlameGraph/flamegraph.pl > flame.svg

# 在浏览器打开 flame.svg

火焰图阅读方法: - X 轴:函数出现频率(宽 = 热点) - Y 轴:调用栈深度 - 颜色:随机(无特殊含义) - 平顶:CPU 实际消耗的地方

perf top — 实时热点

# 类似 top 的实时视图
perf top -p <pid>
# 实时显示哪些函数占 CPU 最多

# 系统级
sudo perf top -g

perf trace — 轻量 strace

# 类似 strace 但开销小得多
perf trace -p <pid>
perf trace -e read,write -p <pid>

# 统计 syscall 延迟
perf trace -s -p <pid> -- sleep 10

高级用法

采样特定事件

# L1 cache miss 触发的采样
perf record -e L1-dcache-load-misses -g ./app

# 缺页中断
perf record -e page-faults -g ./app

# 上下文切换
perf record -e context-switches -g -p <pid> -- sleep 10

# 分支预测失败
perf record -e branch-misses -g ./app

perf annotate — 指令级分析

perf record -g ./app
perf annotate <function_name>
# 显示汇编指令级的采样分布
# 精确定位到哪条指令最热

perf lock — 锁竞争分析

perf lock record ./app
perf lock report
# 显示每个锁的等待时间、竞争次数

perf sched — 调度分析

perf sched record -- ./app
perf sched latency
# 显示每个任务的调度延迟

perf sched map
# 可视化 CPU 时间线

perf mem — 内存访问分析

perf mem record ./app
perf mem report
# 显示内存访问延迟分布、NUMA 远端访问比例

行为与语义

采样原理

perf record 工作方式:
  1. 设置硬件 PMU 计数器,每 N 个事件溢出一次
  2. 溢出时触发 NMI(不可屏蔽中断)
  3. 记录当时的 PC(程序计数器)和调用栈
  4. 统计 → 函数 X 被采样 M 次 ≈ 占了 M/Total 的时间

不是精确测量,是统计采样(但样本足够多时非常准确)

开销

perf stat: 几乎为 0(只读硬件计数器)
perf record -F 99: ~1% CPU 开销
perf record -F 999: ~5% CPU 开销
perf record -F 9999: ~20% 开销(频率太高)

生产环境推荐: -F 99 或 -F 499

常见陷阱与 FAQ

1. 需要调试符号

# 没有符号表看到的是地址而非函数名
# 安装 debuginfo 包或编译时加 -g
gcc -g -O2 app.c -o app

# 内核符号
cat /proc/kallsyms | head  # 需要 root

2. perf_event_paranoid 限制

cat /proc/sys/kernel/perf_event_paranoid
# -1: 无限制
#  0: 允许 raw tracepoints
#  1: 允许 CPU 事件(默认)
#  2: 只允许用户态事件
#  3: 不允许

# 临时降低限制
sudo sysctl kernel.perf_event_paranoid=-1

3. 采样偏差 (skid)

PMU 溢出到 NMI 处理之间有几条指令延迟
  → 采样点可能偏移几条指令
  → 在函数边界处可能归到"错误"的函数

解决:使用 PEBS/LBR (Intel) 精确采样
perf record --call-graph lbr ./app

4. 内核函数看到 [unknown]

# 需要 root 权限查看内核符号
sudo perf record -g ./app
sudo perf report

# 或设置
echo 0 > /proc/sys/kernel/kptr_restrict

观测与调试

# 查看可用的 perf 事件列表
perf list

# 查看硬件 PMU 支持
perf list hw
perf list cache

# 查看 tracepoint
perf list tracepoint

# 快速一行命令找热点
perf top -p <pid> -g --no-children

延伸阅读