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 — 实时热点¶
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 lock — 锁竞争分析¶
perf sched — 调度分析¶
perf mem — 内存访问分析¶
行为与语义¶
采样原理¶
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
延伸阅读¶
man 1 perf— 概述man 1 perf-stat/perf-record/perf-report- Brendan Gregg 的 perf Examples
- FlameGraph