每周性能技巧 #53:使用硬件性能计数器精确测量 C++ 基准

本节阅读量:

本文翻译自 Abseil 官网的 Performance Tip of the Week #53: Precise measurement of C++ benchmarks using hardware performance counters

原文发布于 2021 年 11 月 2 日,作者 Dmitry Vyukov 和 Felix Homann,更新于 2025 年 7 月 16 日。

硬件性能计数器提供了比运行时间更细的视角。它们可以告诉我们某段代码执行了多少指令、产生多少 cache miss、分支预测失败多少次,或消耗了多少周期。本篇介绍如何把它们用于 C++ benchmark。

为什么使用性能计数器

墙钟时间会受到系统负载、频率变化、调度和噪声影响。性能计数器并不能消除所有问题,但能帮助解释时间变化背后的原因。

例如,如果改动后时间下降,同时指令数和 cache miss 也下降,我们更有信心它是真实优化。如果时间下降但计数器没有变化,可能是噪声或测试环境差异。如果指令数下降但 cache miss 上升,生产结果可能取决于真实工作负载。

常用计数器

常用事件包括:

  • cycles:处理器周期。
  • instructions:retire 的指令数。
  • branches 和 branch-misses:分支数量和预测失败。
  • cache-references 和 cache-misses:缓存访问和 miss。
  • L1、LLC、TLB 相关事件。

不同处理器支持的事件不同,命名也不同。使用时应确认事件语义,并避免在不同架构间过度比较。

与 benchmark 结合

在 Linux 上可以用 perf stat 包裹 benchmark:

1
perf stat -e cycles,instructions,branches,branch-misses,cache-misses ./my_benchmark

也可以使用 benchmark 框架或内部工具,在每个 benchmark 运行期间采集 PMU 数据,并把结果与运行时间一起报告。

注意事项

性能计数器也有陷阱:

  • 事件可能复用,导致测量不是完全同时发生。
  • 计数器可能受内核、其他线程或调度影响。
  • 某些事件是近似或架构相关的。
  • 高频采样或过多事件会增加开销。

因此,计数器最好与时间、汇编检查、profile 和生产数据一起使用。

结语

硬件性能计数器能让微基准更可解释。它们帮助我们区分“真的做了更少工作”和“只是这次跑得快”,也帮助发现时间指标背后的瓶颈。

上一篇:每周性能技巧 #39:警惕带着礼物来的微基准测试

上一节

下一篇:每周性能技巧 #9:过了黄金期的优化

下一节