用cachegrind做前后对比必须显式指定固定文件名(如profile_before/profile_after),禁用随机PID命名;两次运行需完全一致,仅输出名和程序参数可变;再用cg_diff分析函数级D1 misses变化,重点关注±1000以上增量,并结合D1 misses/Ir比值与真实机器验证。

用 cachegrind 做前后对比必须固定输出文件名
不加 --cachegrind-out-file,两次运行会生成带 PID 的随机文件(如 cachegrind.out.12345),cg_diff 根本无法识别哪是 before、哪是 after。直接手动比数字等于白跑。
必须显式指定文件名,且注意:
- 文件名不能含路径分隔符(比如 ./profile/before 会静默失败,只写默认名)
- 推荐用简洁无扩展名的名称,例如 profile_before 和 profile_after
- 两次命令要完全一致,除了输出名和程序参数(如有)
正确写法:
valgrind --tool=cachegrind --cachegrind-out-file=profile_before ./a.out valgrind --tool=cachegrind --cachegrind-out-file=profile_after ./a.out
用 cg_diff 看函数级 D1 misses 增减量
cg_diff 是唯一能对齐函数/行号做差值分析的工具。它不看总 miss 数,而是逐函数计算增量,过滤掉编译器抖动带来的噪声。
执行后重点关注:
- 开头带
+123或-456的行:表示该函数 D1 misses 绝对值变化量(不是百分比) -
+N且N > 1000的函数才值得深挖;±20 这类波动大概率是 CPU 频率浮动或 cache 预热差异导致 - 如果某函数 D1 misses ↓ 但
Ir(指令数)↑ 明显,说明你用了空间换时间策略,得实测延迟是否真降
别只盯 miss rate,重点看 D1 misses / Ir 比值
D1 miss rate = D1 misses / total memory refs 很容易被误导——因为 total memory refs 本身会随向量化、循环展开剧烈变化。
更稳的指标是每条指令引发的 L1 数据缓存未命中数:
- 它对编译器优化不敏感
- 能真实反映访存效率是否提升
- 可用 cg_annotate --auto=yes profile_after | head -20 提取 top 20 行,确认下降是否集中在你改的函数里
如果优化后 L2d misses 反而上升,可能是 L1 压力减小导致更多请求穿透到 L2,此时要查 L2d miss rate 是否同步下降,否则不算有效优化。
真实机器验证前,先控住变量
Cachegrind 模拟的是固定大小缓存(默认 D1=32KB),但它不模拟预取器、TLB、内存控制器排队、NUMA 跨节点延迟。所以模拟结果只是参考,上线前必须在真实机器上复现。
做 cachegrind 对比时,务必保证:
- 同一台物理机(避免不同 CPU 微架构干扰)
- 关闭 turbo boost 或锁频(
echo 1 | sudo tee /sys/devices/system/cpu/intel_pstate/no_turbo) - 输入数据完全一致(包括文件、命令行参数、环境变量)
- 运行前清空页缓存:
sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"
哪怕只差一个环境变量,D1 misses 绝对值也可能漂移 ±5%,掩盖真实优化效果。


















