Valgrind的cachegrind不直接提供D1数据缓存命中率,需手动计算:命中率=(D1 reads + D1 writes − D1 misses)/(D1 reads + D1 writes);应忽略旧版中的D1 miss rate字段,只信任D1 reads、writes、misses三个原始计数。

Valgrind 的 cachegrind 不直接显示“数据缓存命中率”,只给原始计数,你得自己算。
怎么看 D1 缓存读写与缺失数
cachegrind 输出里真正反映数据缓存行为的是 D1 相关字段:它对应一级数据缓存(Data Cache),不是指令缓存(I1)或末级缓存(LL)。关键字段包括:
-
D1 reads:D1 缓存读访问总次数 -
D1 writes:D1 缓存写访问总次数 -
D1 misses:D1 缓存缺失总次数(含读缺失D1mr和写缺失D1mw,但报告中常合并为一个数)
运行后用 cg_annotate 提取汇总信息最可靠:
cg_annotate cg.out --auto=yes | head -20
你会看到类似:
I1 misses: 614 D1 misses: 2041 D1 reads: 95613 D1 writes: 28789
注意:D1 misses 是总数,不区分读/写;如果需要细粒度(比如只看写缺失),得解析原始 cg.out 文件中带 D1mr/D1mw 的行——但绝大多数优化场景只关心总缺失率。
怎么算 D1 数据缓存命中率
命中率不是内置指标,必须手动计算:
- 总 D1 访问数 =
D1 reads+D1 writes - D1 命中数 = 总访问数 −
D1 misses - D1 命中率 = (
D1 reads+D1 writes−D1 misses) / (D1 reads+D1 writes)
例如上面的数据:(95613 + 28789 − 2041) / (95613 + 28789) ≈ 98.3%。别用 LL misses 去除 LL refs——那不是“L3 命中率”,而是未命中末级缓存的访存占比,漏掉了在 L1/L2 就命中的部分。
为什么不能直接信 D1 miss rate 这个字段
旧版 Valgrind(如 3.5.0)在摘要里会打印类似 D1 miss rate: 1.6% ( 0.6% + 4.9% ),但它有严重误导性:
- 括号里的两个百分比分别对应读缺失率和写缺失率,但分母不同(读缺失率用
D1 reads作分母,写缺失率用D1 writes),无法合成一个有意义的总体缺失率 - 新版 Valgrind(3.12+)已移除该行,只保留原始计数,就是因为它容易引发误读
- 如果你看到这个字段,忽略它,只信
D1 misses、D1 reads、D1 writes三个整数
实际分析时最容易被忽略的点
真实程序里,D1 数据访问统计受编译器行为影响极大:
- 函数内联后,
cachegrind把访存计数归到调用点,而不是原函数定义处——所以别指望按函数名直接定位热点,得结合--line=yes看具体行号 - 用
-O2没问题,但开了-flto会导致符号丢失,cachegrind无法关联源码行,必须禁用 - 所有测试必须加
-g编译,否则行号映射错乱;缓存参数(--I1=...等)建议显式指定,否则默认值可能和你的 CPU 不符,导致模拟失真
最常出错的,是拿 LL misses / LL refs 当“L3 命中率”去优化——其实它连“末级缓存命中率”都不是,只是未命中末级缓存的访存占比。盯死 D1 才是看数据局部性的正路。


















