Appearance
perf annotate —— 汇编级性能分析
更新时间:2026-08-06
perf annotate 将热点函数的汇编指令逐行展示,并标注每条指令的 CPU 开销占比,回答"热点函数的哪条汇编指令最慢"。
关联:perf-report.md(找到热点函数)、perf-record.md(采样)、CPU 微架构(理解指令级瓶颈)
一、perf annotate 能回答什么问题
| 问题 | 怎么看 |
|---|---|
| 热点函数里哪行代码最慢? | annotate 视图,找开销最高的指令 |
| 是计算指令慢还是访存指令慢? | 看 mov(访存)vs add/mul(计算)的开销占比 |
| 是否有 cache-miss 导致的延迟? | perf record -e cache-misses + annotate |
| 循环展开是否生效? | 对比优化前后的汇编开销分布 |
| 分支预测错误集中在哪? | perf record -e branch-misses + annotate |
二、基本使用
2.1 进入 annotate
bash
# 先采样
perf record -g ./your_program
# 方式 1:分析全部函数
perf annotate
# 方式 2:只分析指定函数
perf annotate -d hot_function
# 方式 3:在 perf report 中按 'a' 进入选中函数的 annotate
perf report
# 光标移到热点函数上 → 按 a2.2 指定事件
bash
# 以 cache-misses 事件采样
perf record -e cache-misses ./your_program
perf annotate -d hot_function三、annotate 输出解读
bash
Percent │ Source code & Disassembly of your_program
│ :
│ int hot_function() {
│ long sum = 0;
│ for (int i = 0; i < 1000000; i++) {
│ sum += data[i];
0.12 │ mov -0x8(%rbp),%eax
0.05 │ cmp $0xf4240,%eax
0.08 │ jg 28
0.15 │ mov -0x8(%rbp),%eax
0.18 │ cltq
45.23 │ mov -0x18(%rbp,%rax,8),%rdx # ← 热点!
0.42 │ mov -0x10(%rbp),%rax
38.56 │ add %rdx,%rax # ← 热点!
5.21 │ mov %rax,-0x10(%rbp)
4.87 │ addl $0x1,-0x8(%rbp)
5.13 │ jmp 10
│ }
│ return sum;
│ }| 列 | 含义 |
|---|---|
| Percent | 该条指令占该函数采样的百分比 |
| ← 热点 | 开销最高的指令——即性能瓶颈 |
解读:上例中
mov(从 data[i] 读)和add(累加)合计 83.79%,说明 CPU 时间主要花在"读内存 + 累加"上——典型的 O0 未优化特征。
源码对照
如果编译时加了 -g,annotate 会显示 源代码 → 汇编 → 占比 三栏对照:
bash
Percent │ Source code & Disassembly
│ :
│ // sum += data[i];
45.23 │ mov -0x18(%rbp,%rax,8),%rdx # 读 data[i]
38.56 │ add %rdx,%rax # sum += data[i]四、annotate TUI 操作
| 按键 | 作用 |
|---|---|
↑ ↓ | 移动 |
Enter | 跟踪跳转目标 |
Esc | 返回 |
h | 切换源码/汇编显示 |
t | 切换百分比/采样数显示 |
j / k | 上一个/下一个函数 |
q | 退出 |
五、实战场景
场景 1:判断热点是"算得慢"还是"读得慢"
打开 annotate,看百分比最高的指令:
| 指令类型 | 含义 | 优化方向 |
|---|---|---|
mov / movsd 高 | 大量时间花在读取数据 | 预取、缓存对齐、减少数据量 |
add / mul / fma 高 | 大量时间花在计算 | SIMD 向量化、算法优化 |
jmp / je / jne 高(且分支预测错误多) | 分支预测压力 | 消除分支、用查表代替 |
div / idiv 高 | 除法指令极慢 | 用乘法倒数代替(a / b → a * (1.0/b)) |
场景 2:对比 O0 vs O2 汇编
bash
# O0 版本
perf record ./prog_o0 && perf annotate -d hot_function > o0.txt
# O2 版本
perf record ./prog_o2 && perf annotate -d hot_function > o2.txt
# diff 对比
diff o0.txt o2.txtO2 下常见的优化效果:
mov+add被融合为 SIMD 指令- 循环展开,跳转指令大幅减少
- 变量常驻寄存器,不再反复从栈读取
场景 3:cache-miss 级别的定位
bash
perf record -e cache-misses ./your_program
perf annotate -d hot_function可以看到 cache-miss 集中在哪些 load 指令上,进而分析该 load 对应的数据访问模式。
六、注意事项
- 需要调试符号:编译时必须加
-g,否则无法显示源码对照,只能看纯汇编。 - 加
-fno-omit-frame-pointer可提高调用栈回溯精度。 - O2 优化下的行号可能不准:编译器优化可能导致一条源码行对应多条分散的汇编指令。
- 采样精度:用
perf record -e cycles:pp启用 PEBS 精确采样,减少 skid(采样位置偏差)。
七、与相关文档的交叉引用
| 场景 | 文档 |
|---|---|
| 先找到热点函数 | perf-report.md |
| 理解为什么要重新采样 | perf-record.md |
| 理解指令级瓶颈(流水线、乱序) | CPU 微架构 |
| O0 vs O2 完整案例 | perf 案例集 |
八、一句话总结
perf annotate是性能分析的"显微镜"——把热点函数逐条汇编指令拆开,精确标注每条指令的 CPU 开销,让你看清"慢在哪里"而非只知道"这个函数慢"。