文档目录

一、基本使用

# 以 99Hz 频率采样,记录 CPU 正在执行的位置
perf record -F 99 -g ./my_program
# -F 99:每秒采样 99 次(避免采样开销过大)
# -g:记录调用栈(才能看到谁调了谁)

# 查看热点函数排名
perf report --stdio

# 输出示例:
# Samples: 1K of event 'cycles', 4000 Hz
# Event count (approx.): 845117845
# Overhead  Command   Shared Object     Symbol
#   25.32%  program   program           [.] compute_dct
#   18.15%  program   program           [.] motion_search
#   12.07%  program   program           [.] avcodec_send_frame
#    8.50%  program   libc-2.31.so      [.] memcpy
#    5.23%  program   program           [.] frame_copy
#    ...

# 看到热点函数后,用 perf annotate 查看汇编级别的热点
perf annotate compute_dct

二、perf annotate 的汇编级分析

输出示例:

compute_dct:
         │ 循环展开的热点:
         │ dct_4x4.c:25 (循环主体)
         │ vld1.32  {q0}, [r1]!       ← 加载数据
   23.5% │ vmla.f32  q1, q0, q2       ← 乘加运算(占 23.5% 的 CPU 时间)
   12.1% │ vld1.32  {q3}, [r2]!       ← 再加载数据
   18.3% │ vmla.f32  q4, q3, q5       ← 乘加运算
         │ subs     r3, #1
    5.2% │ bne      loop              ← 分支

如果一条指令占的时间异常高(比如 50%+),说明这条指令可能是流水线停滞点。
常见原因:
  - cache miss(加载指令)
  - 除法(div 指令,几十个周期)
  - 浮点转换(fcvt)

三、C++ 实战:用 perf 找到热点函数

// bench.cpp —— 一个需要性能分析的程序
#include <vector>
#include <algorithm>
#include <cmath>

// 模拟视频编码中的运动估计
void motion_search(const std::vector<int>& frame, int& best_mv) {
    best_mv = 0;
    int best_cost = INT_MAX;

    for (int dy = -16; dy <= 16; dy++) {
        for (int dx = -16; dx <= 16; dx++) {
            int cost = 0;
            // 简化的 SAD(绝对差和)计算
            for (int i = 0; i < 64; i++) {
                int diff = frame[i] - frame[i + dy * 64 + dx];
                cost += std::abs(diff);
            }
            if (cost < best_cost) {
                best_cost = cost;
                best_mv = dy * 64 + dx;
            }
        }
    }
}

// 模拟 DCT 变换
void dct_transform(std::vector<float>& block) {
    for (int i = 0; i < 64; i++) {
        block[i] = std::sin(block[i]) * std::cos(block[i]);  // 假运算模拟负载
    }
}

int main() {
    std::vector<int> frame(4096, 128);
    std::vector<float> dct_block(64, 1.0f);

    for (int i = 0; i < 100000; i++) {
        if (i % 10 == 0) {
            dct_transform(dct_block);  // 每 10 次做一次 DCT
        } else {
            int mv;
            motion_search(frame, mv);  // 大多数时间做运动估计
        }
    }
}
# 编译并运行 perf
g++ -O2 -g -o bench bench.cpp
perf record -F 99 -g ./bench
perf report --stdio

# 预期输出:
# Overhead  Symbol
#   72.5%   motion_search    ← 主要热点,占总 CPU 的 72%
#   18.3%   dct_transform    ← 次要热点
#    5.2%   std::abs         ← 被 motion_search 调用的

# 优化方向:把 motion_search 的 -16..16 搜索区域减少、提前终止等