Linux perf stat 怎样区分分支预测失败和缓存未命中
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

这里的重点不是把四个事件拼成一个“综合分数”,而是让两个比例使用同一次运行的上下文。若工作负载有明显冷启动、输入随机性或后台干扰,至少先让它能稳定重复,再比较多次测量的趋势。
用各自的分母读懂两个比例
分支和缓存不能使用同一个分母。最常见的解释方式是:
| 观察对象 | 计算方式 | 能说明什么 | 不能直接说明什么 |
|---|---|---|---|
| 分支预测失败率 | 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

如果命令提示事件不存在,先把它当成平台差异处理,而不是修改标题中的概念。记录 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.htmlhttps://man7.org/linux/man-pages/man1/perf-stat.1.htmlhttps://man7.org/linux/man-pages/man1/perf-list.1.htmlhttps://man7.org/linux/man-pages/man2/perf_event_open.2.html
Linux io_uring 与 epoll 在大量短连接下怎么选择
- 上一篇
- Linux io_uring 与 epoll 在大量短连接下怎么选择
- 下一篇
- nftables 拼接集合怎样按地址和端口同时匹配
-
- 文章 · linux | 14分钟前 | linux运维 · 性能排查 · Linux 性能诊断 PSI memory pressure /proc/pressure/memory
- Linux /proc/pressure/memory 的 avg10 与 total 怎么读
- 443浏览 收藏
-
- 文章 · linux | 18分钟前 |
- systemd PathChanged 怎样触发服务处理文件变更
- 300浏览 收藏
-
- 文章 · linux | 18分钟前 |
- Linux cgroup v2 io.weight 与 io.max 怎么选
- 299浏览 收藏
-
- 文章 · linux | 21分钟前 | systemd ListenStream socket activation ListenDatagram
- systemd ListenStream 与 ListenDatagram 怎么选
- 293浏览 收藏
-
- 文章 · linux | 11小时前 |
- /proc/self/fd 读取进程已打开文件的排查方法
- 494浏览 收藏
-
- 文章 · linux | 11小时前 |
- udev 规则按硬件属性固定设备名称的实现
- 287浏览 收藏
-
- 文章 · linux | 11小时前 | ssh · Linux SSH 连接复用 ControlMaster ControlPath ControlPersist
- SSH ControlMaster 复用连接并限制共享范围的方法
- 176浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 1019次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 1021次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1155次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1122次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 963次使用
-
- Go goroutine 泄漏怎么查:pprof、context 和通道关闭检查清单
- 2026-06-27 392浏览
-
- Go select default 为什么会让 CPU 飙高?从空转循环到可控等待
- 2026-07-02 459浏览
-
- Go 服务锁竞争变慢怎么查:mutex profile 的采样、定位和修复手册
- 2026-07-15 395浏览
-
- Go 1.23 以后还要手动 Stop Timer 吗:一次超时循环改造实战
- 2026-07-16 403浏览
-
- Go 1.25 runtime/trace.FlightRecorder 怎么接:把偶发延迟留在内存环形缓冲里
- 2026-07-26 425浏览

