登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux perf stat 怎样区分分支预测失败和缓存未命中

来源:17golang原创

时间:2026-11-14 06:34:16 418浏览 收藏

Linux 性能排查里,branch-misses 和 cache-misses 经常一起出现,但它们回答的是两件不同的事:前者统计处理器对分支方向的预测失误,后者通常表示某一级缓存访问没有命中,且具体映射会随 CPU 和 PMU 改变。要区分它们,先在同一次 perf stat 测量中保留各自的分子和分母,再结合本机的 perf list 解释事件。

官方地址:https://perf.wiki.kernel.org/

下面的示例只围绕硬件计数器、采样和架构差异展开。假设 ./workload 是已经准备好的、可以重复运行的程序,不把示例命令的数字当成固定运行结果。

先把两类计数器放在同一次测量里

最小的对照组是四个通用事件:

  • branches:已执行或退休的分支指令计数,具体映射由平台决定。
  • branch-misses:分支预测失败的计数。
  • cache-references:缓存访问参考计数,可能包含预取或一致性相关行为。
  • cache-misses:缓存未命中计数,通常接近最后一级缓存未命中,但不能脱离本机 PMU 说明。
# 把分支和缓存的分子、分母放到一次统计中,便于比较同一轮工作负载
perf stat -e '{branches,branch-misses,cache-references,cache-misses}' -- ./workload

# 先查看当前 perf 能解析哪些同名事件和描述,避免直接猜测硬件含义
perf list branch-misses
perf list cache-misses
perf stat 与分支、缓存通用计数器之间关系的静态说明图
图1:perf stat、分支计数器与缓存计数器的静态关系说明图,不是截图或运行证据。

这里的重点不是把四个事件拼成一个“综合分数”,而是让两个比例使用同一次运行的上下文。若工作负载有明显冷启动、输入随机性或后台干扰,至少先让它能稳定重复,再比较多次测量的趋势。

用各自的分母读懂两个比例

分支和缓存不能使用同一个分母。最常见的解释方式是:

观察对象计算方式能说明什么不能直接说明什么
分支预测失败率branch-misses / branches分支中有多少次没有按预测路径命中不能单独换算成总耗时或每次失误的固定代价
缓存未命中率cache-misses / cache-references被计数的缓存参考中有多少没有命中不能默认它只代表 L1,也不能代表所有内存停顿

例如,分支比例明显升高而缓存比例稳定,优先检查条件分支、间接调用、输入分布和循环控制;缓存比例升高而分支比例稳定,优先检查访问局部性、数据布局、工作集大小和指针追踪。两个比例都升高时,不要立刻断言“CPU 同时被两种问题拖慢”,应继续确认事件是否被正确调度以及工作负载是否发生了输入变化。

可以让 perf stat 重复执行同一个命令,用平均值和离散程度观察趋势:

# 重复同一程序五次;固定 CPU 只是减少迁移噪声,不会修正事件语义
taskset -c 2 perf stat -r 5 \
  -e '{branches,branch-misses,cache-references,cache-misses}' \
  -- ./workload

# 只统计用户态,减少内核路径对应用工作负载的混入
perf stat -e 'branches:u,branch-misses:u,cache-references:u,cache-misses:u' -- ./workload

先排除计数器没有完整覆盖

CPU 的硬件计数器数量有限。当请求的事件多于可同时调度的计数器时,perf 会进行时间复用;输出中的运行时间比例会影响计数的可信度。把多个事件放进组可以要求它们尽量一起调度,但事件太多、PMU 不支持组合或计数器有特殊限制时,组也可能无法运行。

# 让四个事件尽量作为同一组调度;组无法满足时应按提示拆成两轮
perf stat -e '{branches,branch-misses,cache-references,cache-misses}' -- ./workload

# 使用详细模式查看事件解析、PMU 和附加指标,辅助解释 not counted 或覆盖率提示
perf stat -v -e '{branches,branch-misses,cache-references,cache-misses}' -- ./workload

遇到 not counted、覆盖率很低或两次运行的启用时间差异很大时,不要拿结果直接做优化结论。可以将事件拆成分支组和缓存组分别运行,并保持输入、CPU 亲和性、程序参数和重复次数一致。这样得到的两轮数字不再是同一时刻,但通常比一个没有完整计数的混合结果更容易解释。

用 perf list 确认架构和 PMU 边界

perf 提供的是通用事件名称,底层仍然由当前 CPU 的 PMU 负责映射。Linux 文档对通用缓存未命中的描述使用了“通常是最后一级缓存”的限定;同一个名字在不同微架构、内核版本或虚拟机环境里,可能对应不同的硬件事件。

# 查看事件的长描述、解析细节以及当前系统暴露的 PMU 名称
perf list --details branch-misses
perf list --details cache-misses
ls /sys/bus/event_source/devices/

# 混合架构可能同时暴露不同 PMU;只有本机列出对应 PMU 时才使用这种限定写法
perf stat -e 'cpu_core/branch-misses/,cpu_core/cache-misses/' -- ./workload
perf stat -e 'cpu_atom/branch-misses/,cpu_atom/cache-misses/' -- ./workload
perf 通用事件映射到 cpu_core 与 cpu_atom PMU 的静态说明图
图2:通用事件别名、PMU 与解释层之间的静态映射图,不是截图或运行证据。

如果命令提示事件不存在,先把它当成平台差异处理,而不是修改标题中的概念。记录 CPU 型号、内核版本、perf 版本和 perf list 中的实际事件名,再选择厂商 PMU 文档里的原生事件。不要把某个 Intel 事件编码原样搬到 AMD、Arm 或虚拟 CPU 上。

需要定位代码时再切换到采样

perf stat 适合回答“这次工作负载发生了多少次、比例怎样”。当比例已经指向分支或缓存问题,但还不知道哪个函数贡献最大时,才用 perf record 采样热点。采样周期、权限和 PMU 支持同样依赖环境,不能把采样点数量当作精确事件总数。

# 以分支误预测事件做采样,周期只是示例,应按本机事件支持情况调整
perf record -e branch-misses:u -c 100000 -- ./workload

# 读取采样报告,观察热点函数而不是把采样数量当成精确总次数
perf report

缓存方向也可以采用相同思路,但更应该先确认采样事件的粒度。某些平台能提供更细的 L1、LLC、加载来源或精确事件,某些平台则只能提供较粗的通用别名。文章标题中的“缓存未命中”因此应理解为一次可解释的计数起点,而不是对所有缓存层的统一定义。

一张表完成结果判断

现象先看什么下一步
branch-misses / branches 偏高输入分布、条件分支、间接跳转用采样或反汇编定位贡献函数,再评估分支改写是否值得
cache-misses / cache-references 偏高访问步长、工作集、数据布局、缓存层级确认具体缓存事件和层级,再比较布局或批处理方案
两个比例都不稳定重复次数、CPU 迁移、输入和事件覆盖率先固定测量条件,必要时拆组分两轮采集
事件不存在或值异常perf list、PMU 名称、虚拟化和权限改用本机支持的通用或原生事件,不复制其他 CPU 的编码

真正有用的结论应该同时写出事件名、分母、运行覆盖率、CPU/内核环境和重复方式。只贴一行 cache-misses 的绝对值,既不能证明是 LLC 问题,也不能证明优化后一定更快。

相关问题

branch-misses 越多就一定越慢吗?

不一定。失误的代价取决于微架构、分支位置、并行度和工作负载,比例只能帮助发现方向,不能直接换算耗时。

cache-misses 能代表 L1 缓存未命中吗?

不能直接这样读。通用事件通常更接近最后一级缓存未命中,但实际定义要以本机 perf 描述和 CPU PMU 文档为准。

为什么 perf stat 输出里会出现百分比或 not counted?

事件可能被时间复用、无法同时调度或受到权限和 PMU 限制。先降低事件数量、拆分测量并检查详细事件解析,再比较结果。

什么时候需要 perf record?

当 stat 已经确认某类计数偏高,但还需要知道是哪个函数或指令位置贡献时,再转向采样;stat 与 record 的数字不能混为同一种度量。

官方参考

  • https://www.kernel.org/doc/html/latest/admin-guide/workload-tracing.html
  • https://man7.org/linux/man-pages/man1/perf-stat.1.html
  • https://man7.org/linux/man-pages/man1/perf-list.1.html
  • https://man7.org/linux/man-pages/man2/perf_event_open.2.html
声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>