应关注Cachegrind输出中D1 miss rate和L2d miss rate数值,括号内写miss率(如4.9%)对性能影响更直接;需用-O0编译、--cachegrind-out-file导出明细,并手动计算D1mr/(Dr+Dw)得真实未命中率。

看 D1 miss rate 和 L2d miss rate 数值
缓存命中率是否合理,不靠猜,直接看 Cachegrind 输出里数据访问的 miss 率。关键指标是 D1 miss rate(一级数据缓存未命中率)和 L2d miss rate(二级数据缓存未命中率),它们出现在输出块的 “D refs” 段落末尾,格式类似:D1 miss rate: 1.6% ( 0.6% + 4.9% )。
数值越低越好,尤其关注括号里的写 miss 率(如 4.9%),它对性能影响更直接。对比两个循环版本时,若一个的 D1 miss rate 是 0.3%,另一个是 4.2%,基本就能断定前者访存局部性更好。
用 --cachegrind-out-file= 导出详细行级报告
默认终端输出只给汇总,看不出哪一行在拖后腿。加参数导出明细:
valgrind --tool=cachegrind --cachegrind-out-file=cg.out ./test1
然后用 cachegrind_annotate cg.out 查看每行代码的 Ir(指令数)、Dr(数据读)、Dw(数据写)、D1mr(D1 miss rate)等列。重点关注嵌套循环体内的赋值语句行,比如 array[i][j] = i+j; 这一行的 D1mr 值。
- 若内层循环变量是第二维(
j),且数组是 row-major 存储(C/C++ 默认),那i变、j变的写操作大概率连续命中 cache line - 反之,若内层是
i,每次写都跳SIZE * sizeof(int)字节,D1mr会明显升高
注意编译优化会掩盖问题
gcc 开 -O2 或更高优化时,编译器可能重排循环、向量化、甚至把整个数组计算优化掉,导致 Cachegrind 报告失真 —— 看起来 miss 率都很低,但实际没反映原始逻辑的访存特征。
做 cache 局部性分析时,必须关掉优化:
- 用
gcc -g -O0编译 - 确保测试程序没有被 inlining、loop unrolling 等干扰
- 如果用了
#if 0 / #endif切换循环顺序,要确认预处理后生成的 AST 确实不同
cg_annotate 的 D1mr 列单位不是百分比
这是最容易踩的坑:cachegrind_annotate 输出中,D1mr 列显示的是“该行引发的 D1 miss 次数”,不是百分比。它和 Dr、Dw 同为绝对计数,需手动算比率(D1mr / (Dr + Dw))才对应命令行里看到的 D1 miss rate。
例如某行显示 1000 5000 2000 120(对应 Ir Dr Dw D1mr),那它的 D1 miss rate 是 120 / (5000 + 2000) ≈ 1.7%。不换算就直接比数字,会误判。
真正影响性能的,是那个算出来的比率,不是 raw D1mr 值本身;而这个比率,在终端 summary 里已经帮你算好了,别绕远路。


















