关键不是看“哪个 miss 少”,而是看同一硬件、编译条件、运行路径下哪些指标变化有统计意义;需用--cachegrind-out-file固定输出名,再用cg_diff分析函数级d1 misses增减量,并优先关注d1 misses/ir比值及真实机器验证。

直接对比 cachegrind 输出的原始数字容易误判,关键不是看“哪个 miss 少”,而是看「同一硬件、同一编译条件、同一运行路径下,哪些指标变化有统计意义」。
用 --cachegrind-out-file 固定输出路径再 diff
默认情况下 cachegrind 会生成带 PID 的文件(如 cachegrind.out.12345),手动找、手动比极易出错。必须强制指定输出名:
valgrind --tool=cachegrind --cachegrind-out-file=profile_before ./a.out valgrind --tool=cachegrind --cachegrind-out-file=profile_after ./a.out
- 不加
--cachegrind-out-file,两次运行的输出文件名不同,cg_diff工具根本没法识别差异来源 - 文件名不能含路径分隔符(如
./out/before)——cachegrind会静默失败,只生成默认命名文件 - 确保两次运行的输入数据、环境变量、CPU 频率(避免 turbo boost 波动)一致,否则
D1 misses的绝对值波动可能达 ±5%
用 cg_diff 看函数级 cache miss 变化量
cachegrind 自带的 cg_diff 是唯一能可靠做差值分析的工具,它不比较总 miss 数,而是按函数/源码行对齐后算增量:
cg_diff profile_before profile_after > diff.txt
- 输出中每行开头的
+123或-45表示该函数的D1 misses增减量,不是百分比 - 重点关注
+N且 N > 1000 的函数——小数值波动(如 ±20)大概率是噪声 - 如果某函数
D1 misses下降但I refs上升明显,说明你用空间换时间,得结合实际延迟判断是否真优化
别只盯 D1 miss rate,优先看 D1 misses / instruction ratio
单纯看 D1 miss rate: 1.6% 没意义:rate = misses / total memory refs,而 total memory refs 本身会随循环展开、向量化剧烈变化。更稳的指标是:
-
D1 misses / Ir(即每条指令引发的 L1 数据缓存未命中数),这个比值对编译器优化不敏感 - 用
cg_annotate --auto=yes profile_after | head -20提取 top 20 行,看是否集中在你改动的函数内 - 如果优化后
L2d misses反而上升,可能是 L1 压力减小导致更多请求穿透到 L2,需查L2d miss rate是否同步下降
真实场景里最常被忽略的一点
cachegrind 模拟的是固定大小的缓存(如默认 I1/D1 各 32KB,L2 为 2MB),但它**不模拟预取器、TLB、内存控制器排队、NUMA 跨节点访问延迟**。你看到 D1 misses 降了 30%,实际在物理机上 latency 可能只降 5%——尤其当程序原本就受内存带宽限制时。上线前务必在真实机器上用 perf stat -e cache-misses,cache-references,instructions 验证。











