必须加 --branch-sim=yes 才能启用分支模拟,否则无分支数据;启用后输出 Bp(分支总数)和 Bm(误预测数),仅全局汇总,不支持源码级分析,且模型简单,精度有限。

cachegrind --branch-sim=yes 才能启用分支模拟
默认情况下,cachegrind 不统计分支预测行为,你看到的输出里压根没有分支相关字段。必须显式加 --branch-sim=yes 参数,它才会开启分支预测器模拟(基于简单两状态饱和计数器模型),并报告分支误预测次数。
常见错误是只运行 valgrind --tool=cachegrind ./a.out,然后翻遍输出找“branch”或“predict”,结果什么都没有——因为没开开关。
- 正确命令示例:
valgrind --tool=cachegrind --branch-sim=yes --cachegrind-out-file=cg.out ./a.out - 不加
--branch-sim=yes,后续所有分析都无分支数据 - 该选项会略微增加运行开销,但对缓存统计本身无影响
cg_annotate 输出里找 Bp 和 Bm 字段
启用分支模拟后,cachegrind 会在汇总头里新增两行:Bp(分支指令总数)和 Bm(分支误预测数)。它们不出现在逐行注解中,只在顶层摘要里。
运行 cg_annotate cg.out | head -30,你会看到类似:
I1 cache: 32768 B, 8-way, 64 B blocks D1 cache: 32768 B, 8-way, 64 B blocks LL cache: 8388608 B, 16-way, 64 B blocks I1 misses: 1234 D1 misses: 5678 Bp: 98765 Bm: 4321
注意:Bm / Bp 就是模拟出的分支误预测率;cachegrind 不提供“正确预测数”或“条件分支/间接分支分开统计”,只有这两个总值。
-
Bp包含所有控制流改变指令:jmp、call、ret、条件跳转等 -
Bm是模拟器根据历史行为推断出的误预测次数,不是硬件真实值 - 如果代码大量使用
indirect call或switch表跳转,Bm偏差可能较大
分支数据不能按函数/源码行展开
cachegrind 的分支统计是全局汇总的,cg_annotate 不支持像缓存访问那样用 --auto=yes 标出每行代码的 Bp 或 Bm。你无法知道是哪个 if 或哪个 for 循环导致了高误预测率。
这是 cachegrind 的明确限制,不是你操作错了。
- 想定位具体分支点?得换工具,比如
perf record -e branches,uops_retired.any+perf report -
callgrind也不提供分支预测数据,它专注调用图和指令计数(Ir) - 若必须用 valgrind 生态,可结合
callgrind找热点函数,再人工检查其中的分支结构
分支模拟精度有限,别当真值用
cachegrind 的分支预测器是极简模型:每个分支一个 2-bit 饱和计数器,不区分静态/动态分支,不建 BTB(分支目标缓冲区),也不模拟现代 CPU 的 TAGE 或 Loop Predictor 等机制。
它适合发现明显问题(比如一个循环里反复跳转且模式混乱),但不适合微调或对比不同分支写法的细微差异。
- 真实 CPU 的分支预测准确率通常 >95%,而 cachegrind 模拟值可能偏低(尤其对小循环)
- 启用
--branch-sim=yes后,I1 refs和D1 refs数值不变,但整体运行时间会延长约 10–20% - 如果你的程序本身分支极少(如数值计算密集型),
Bm几乎为 0,这时看它没意义
Bm/Bp 只是个粗略信号,不是判决依据。


















