文档目录

一、基本用法

# 统计程序运行期间的硬件计数器
perf stat ./my_program

# 输出示例:
# Performance counter stats for './my_program':
#
#       1324.56  msec task-clock                # 1.00 CPUs utilized
#            12  context-switches               # 9.06 /sec
#             0  cpu-migrations                 # 0.00 /sec
#           125  page-faults                    # 94.35 /sec
#    4,285,294,201  cycles                      # 3.235 GHz
#    2,142,647,100  instructions                # 0.50  insn per cycle
#      428,529,420  branches                    # 323.5 M/sec
#        8,570,588  branch-misses               # 2.00% of all branches
#      214,264,710  L1-dcache-loads
#       21,426,471  L1-dcache-load-misses       # 10.00% of all L1-dcache accesses
#      107,132,355  cache-references
#       10,713,235  cache-misses                # 10.001 % of all cache refs

二、关键指标解读

instructions per cycle (IPC)
  含义:每个 CPU 周期执行了多少条指令
  理想值:现代 CPU 可以做到 3-4(超标量 + 乱序执行)
  
  IPC > 2:程序运行良好,CPU 利用率高
  IPC 1~2:有轻度流水线停顿
  IPC < 1:严重停顿,可能是缓存未命中、分支预测失败、指令依赖

  ❌ 低 IPC 的程序:
    [周期][周期][周期][周期][周期][周期][周期][周期]
    [指令]      [指令]      [指令]      [指令]
    CPU 大部分时间在等待内存或解决依赖

  ✅ 高 IPC 的程序:
    [周期][周期][周期][周期][周期][周期][周期][周期]
    [指令][指令][指令][指令][指令][指令][指令][指令]
    CPU 利用率接近理论峰值

cache-misses
  含义:L3 缓存未命中的次数
  理想值:< 5%(如果 < 1% 说明数据访问模式非常好)

  > 20%:内存访问模式有问题,需要重构数据结构
  常见原因:链表遍历、红黑树中序遍历、跳跃指针

branch-misses
  含义:分支预测失败的次数占比
  理想值:< 5%

  > 10%:分支模式难以预测
  常见原因:二分搜索中的随机数据、switch-case 中的均匀分布
  优化:用分支友好的算法(如用 cmov 替代分支)、使用 [[likely]]/[[unlikely]]

三、C++ 代码与 perf 计数器的对应

// IPC 低的代码(Cache Miss 严重)
void list_traversal(const std::list<int>& lst) {
    // list 的节点散落在堆中,遍历时大量 Cache Miss
    // perf 会显示 IPC < 1, cache-misses > 20%
    for (auto it = lst.begin(); it != lst.end(); ++it) {
        total += *it;
    }
}

// IPC 高的代码(缓存友好)
void vector_traversal(const std::vector<int>& vec) {
    // vector 连续内存,预取器提前加载
    // perf 会显示 IPC > 2, cache-misses < 1%
    for (auto x : vec) {
        total += x;
    }
}

// 分支预测失败的代码
void unpredictable_branch(const std::vector<int>& data) {
    long long sum = 0;
    for (int x : data) {
        // 如果 data 中的正负数随机分布,分支预测失败率 ~50%
        if (x > 0) sum += x;
    }
}

// 分支预测友好的优化版
void predictable_branch(const std::vector<int>& data) {
    long long sum = 0;
    std::sort(data.begin(), data.end());  // 排序后正数都在前面
    for (int x : data) {
        if (x > 0) sum += x;  // 前一批全命中,后一批全不命中 → 预测率接近 100%
    }
}

如何用 perf 验证优化效果:

# 优化前
g++ -O2 -o test test.cpp && perf stat ./test

# 优化后
# 修改代码,重新编译
g++ -O2 -o test test.cpp && perf stat ./test

# 对比两个输出的 cache-misses 和 IPC,应该明显改善

四、常用 perf stat 组合

# 只看关键指标
perf stat -e cycles,instructions,cache-misses,branch-misses ./program

# 调度器行为分析
perf stat -e context-switches,cpu-migrations,page-faults ./program

# 内存访问分析
perf stat -e L1-dcache-load-misses,LLC-load-misses,LLC-store-misses ./program

# TLB 分析
perf stat -e dTLB-load-misses,iTLB-load-misses ./program