我正在使用 Cachegrind 来检索没有 libc 编译的静态程序的缓存未命中数(只是_start
调用我的 main 函数和 asm 中的退出系统调用)。该程序是完全确定的,指令和内存引用不会从一次运行到另一次运行。缓存与作为替换策略的 LRU 完全关联。
但是,我注意到未命中的数量有时会发生变化。更具体地说,在我转到不同的目录之前,未命中的次数总是相同的:
% cache=8 && valgrind --tool=cachegrind --I1=$((cache * 64)),$cache,64 --D1=$((cache * 64)),$cache,64 --L2=262144,4096,64 ./adpcm
...
==31352== I refs: 216,145,010
...
==31352== D refs: 130,481,003 (95,186,001 rd + 35,295,002 wr)
==31352== D1 misses: 240,004 ( 150,000 rd + 90,004 wr)
==31352== LLd misses: 31 ( 11 rd + 20 wr)
如果我一次又一次地执行相同的命令,我将继续得到相同的结果。但是如果我从不同的目录运行这个程序:
% cd ..
% cache=8 && valgrind --tool=cachegrind --I1=$((cache * 64)),$cache,64 --D1=$((cache * 64)),$cache,64 --L2=262144,4096,64 ./malardalen2/adpcm
...
==31531== I refs: 216,145,010
...
==31531== D refs: 130,481,003 (95,186,001 rd + 35,295,002 wr)
==31531== D1 misses: 250,004 ( 160,000 rd + 90,004 wr)
==31531== LLd misses: 31 ( 11 rd + 20 wr)
我什至从不同的目录得到不同的结果。
我还用 Pin 工具做了一些实验,使用这个我不需要更改目录来获取不同的值。但似乎可能值的集合非常有限,并且与 Cachegrind 完全相同。
我的问题是:这种差异的根源是什么?
我的第一个提示是我的程序在内存中的对齐方式不同,因此,在之前的运行中存储在同一行中的一些变量不再存在。这也可以解释有限数量的组合。但我虽然缓存研磨(和 Pin)正在使用虚拟地址,但我假设操作系统(Linux)总是提供相同的虚拟地址。还有什么想法吗?
编辑:您可以猜到读取 LLd 未命中,该程序仅使用 31 个不同的缓存行。此外,缓存只能包含 8 个缓存行。因此,即使在现实中,这种差异也无法通过第二次填充缓存的想法来解释(在最大情况下,L1 中只能保留 8 行)。
编辑 2: Cachegrind 的报告不是基于实际的缓存未命中(由性能计数器给出),而是模拟的结果。基本上,它模拟缓存的行为以计算未命中的数量。由于结果只是暂时的,这完全没问题,并且允许更改缓存属性(大小、关联性)。
编辑 3:我使用的硬件是 Linux 3.2 x86_64 上的 Intel Core i7。编译标志是 -static 并且对于某些程序 -nostdlib (IIRC,我现在不在家)。