2. perf工具入门:安装、事件查看与性能计数器统计
perf 是 Linux 内核官方提供的性能分析工具集,基于 perf_event_open 系统调用和内核事件子系统实现。它能够以极低的开销采集 CPU 周期、缓存命中率、分支预测、软硬件事件以及 tracepoint 数据,是系统级性能分析的起点。本章将带你完成 perf 的安装、事件枚举和基础统计操作。
2.1 perf 的安装与验证
perf 通常随内核源码一同发布,但需要单独安装用户态工具。不同发行版的安装命令如下:
| 发行版 | 安装命令 | 说明 |
|---|---|---|
| Ubuntu / Debian | sudo apt install linux-tools-common linux-tools-$(uname -r) |
需匹配当前内核版本 |
| RHEL / CentOS / Fedora | sudo yum install perf 或 sudo dnf install perf |
EPEL 仓库可能需额外启用 |
| openSUSE | sudo zypper install perf |
默认包含在 kernel-source 中 |
| Arch Linux | sudo pacman -S perf |
来自 linux-tools 组 |
安装完成后,验证 perf 是否可用:
perf --version
# 输出示例:perf version 6.8.0
提示:如果提示
WARNING: perf not found for kernel x.x.x,通常是因为缺少与当前内核版本完全匹配的 linux-tools-$(uname -r) 包。可尝试安装 linux-tools-generic 或从源码编译。
2.2 perf list:查看所有可用事件
perf list 是探索 perf 能力的第一道门。它列出当前系统支持的所有性能事件,包括硬件事件、软件事件、tracepoint 事件和动态 PMU 事件。
基本用法:
perf list
# 输出按类别分组,例如:
# List of pre-defined events (to be used in -e):
# cpu-cycles OR cycles [Hardware event]
# instructions [Hardware event]
# cache-references [Hardware event]
# cache-misses [Hardware event]
# context-switches OR cs [Software event]
# page-faults OR faults [Software event]
# sched:sched_switch [Tracepoint event]
# block:block_rq_complete [Tracepoint event]
常用过滤选项:
| 命令 | 作用 |
|---|---|
perf list hw |
仅列出硬件事件 |
perf list sw |
仅列出软件事件 |
perf list cache |
仅列出缓存相关事件 |
perf list tracepoint |
仅列出 tracepoint 事件 |
perf list sched: |
列出所有调度相关的 tracepoint |
perf list | grep -i "cache" |
搜索包含 "cache" 的事件 |
关键概念:硬件事件由 CPU 性能监控单元(PMU)直接计数,精度高、开销极低;软件事件由内核计数器模拟,适用于上下文切换、缺页等场景;tracepoint 事件是内核静态插桩点,可捕获函数调用栈和参数。
下面这张 SVG 图展示了 perf 事件体系的层级结构:
2.3 perf stat:统计性能计数器
perf stat 是 perf 最基础的计数工具。它启动一个目标程序(或附加到已运行进程),在程序执行期间收集指定事件的计数器值,并在程序退出时汇总输出。
基本语法:
perf stat [options] command [args...]
典型示例——统计 ls 命令的 CPU 周期和指令数:
perf stat ls
# 输出示例:
# Performance counter stats for 'ls':
#
# 1.234567 task-clock (msec) # 0.123 CPUs utilized
# 12 context-switches # 0.010 M/sec
# 0 cpu-migrations # 0.000 K/sec
# 45 page-faults # 0.036 M/sec
# 3,456,789 cycles # 2.800 GHz
# 2,100,000 instructions # 0.61 insn per cycle
# 450,000 branches # 364.567 M/sec
# 12,345 branch-misses # 2.74% of all branches
#
# 0.010234567 seconds time elapsed
常用选项:
| 选项 | 作用 | 示例 |
|---|---|---|
-e event1,event2 |
指定要统计的事件列表 | perf stat -e cycles,instructions ls |
-p PID |
附加到指定进程 ID | perf stat -p 1234 sleep 5 |
-a |
统计整个系统的计数器 | perf stat -a sleep 1 |
-r N |
重复运行 N 次并取平均值 | perf stat -r 5 ./test |
-I 1000 |
每 1000ms 打印一次计数 | perf stat -I 1000 -e cycles sleep 5 |
--detailed |
输出更详细的计数器信息 | perf stat --detailed ls |
注意:使用
-a 全系统模式需要 root 权限或 perf_event_paranoid 设置为较低值。可通过 sudo perf stat -a sleep 1 执行。如果遇到 Access to performance monitoring and observability operations is limited 错误,请检查 /proc/sys/kernel/perf_event_paranoid 的值。
2.4 实战:使用 perf stat 分析程序瓶颈
下面通过一个具体案例演示如何用 perf stat 定位性能瓶颈。假设我们有一个计算密集型的 C 程序 matrix_mult:
// matrix_mult.c
#include <stdio.h>
#define N 1024
double a[N][N], b[N][N], c[N][N];
int main() {
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
for (int k = 0; k < N; k++)
c[i][j] += a[i][k] * b[k][j];
return 0;
}
编译并运行 perf stat:
gcc -O2 -o matrix_mult matrix_mult.c
perf stat -e cycles,instructions,cache-misses,cache-references,branch-misses ./matrix_mult
输出解读:
| 指标 | 值 | 分析 |
|---|---|---|
| cycles | 12,345,678,900 | 总 CPU 周期数 |
| instructions | 8,765,432,100 | 总指令数 |
| IPC | 0.71 | 每周期指令数偏低,可能存在访存停顿 |
| cache-misses | 450,000,000 | 缓存未命中次数高,内存访问是瓶颈 |
| cache-references | 1,200,000,000 | 缓存引用总数 |
| cache-miss rate | 37.5% | 缓存未命中率过高,需优化数据局部性 |
分析结论:该程序的 IPC 仅为 0.71,且缓存未命中率高达 37.5%,说明 CPU 大量时间在等待内存数据。优化方向应为循环分块(tiling)或使用缓存友好的矩阵乘法算法(如 OpenBLAS)。
下面这张 SVG 图展示了 perf stat 的工作流程:
2.5 常见问题与排查
- perf_event_paranoid 限制:如果遇到
perf_event_open权限错误,检查/proc/sys/kernel/perf_event_paranoid。值为 2 时禁止非 root 用户使用硬件事件;值为 1 时允许非 root 用户使用软件事件;值为 0 或 -1 时完全开放。 - 虚拟化环境:在虚拟机中,硬件 PMU 可能未透传,导致硬件事件计数为 0 或不可用。此时应优先使用软件事件和 tracepoint。
- 事件别名:perf 支持事件别名,如
cycles等价于cpu-cycles,cs等价于context-switches。使用perf list可查看所有别名。 - 多事件统计:perf stat 默认同时统计多个事件,但硬件事件受限于 PMU 计数器数量(通常 4~8 个)。超出时内核会进行事件复用(multiplexing),输出中会显示
not counted或复用比例。
最佳实践:首次分析一个程序时,先使用
perf stat -d 获取默认的详细计数器集合(包括缓存和 TLB 事件),快速定位瓶颈方向,再使用 -e 精确指定事件进行深入分析。