缓存一致性系统会尽最大努力向您隐藏此类内容。我认为您必须间接观察它,要么使用性能计数寄存器来检测缓存未命中,要么通过高分辨率计时器仔细测量读取内存位置的时间。
该程序在我的 x86_64 机器上运行,以演示以下效果clflush
。它计算读取全局变量所需的时间rdtsc
。作为直接与 CPU 时钟相关的单个指令,可以直接使用rdtsc
非常适合这个。
这是输出:
took 81 ticks
took 81 ticks
flush: took 387 ticks
took 72 ticks
您会看到 3 次试验:第一个确保i
位于缓存中(确实如此,因为它只是作为 BSS 的一部分被归零),第二个是读取i
那应该在缓存中。然后clflush
kicks i
出缓存(及其邻居)并显示重新读取它需要更长的时间。最终读取验证它是否已返回缓存。结果的重现性非常好,并且差异足够大,足以轻松查看缓存未命中情况。如果您想校准的开销rdtsc()
你可以让差异变得更加明显。
如果你无法读取你想要测试的内存地址(尽管即使mmap
of /dev/mem
应该适用于这些目的)如果您知道缓存的缓存行大小和关联性,您也许能够推断出您想要的内容。然后,您可以使用可访问的内存位置来探测您感兴趣的集合中的活动。
源代码:
(1. Use static inline
或者参考其他方法here如果使用较新的 gcc
2. 灵感来自comment,更好地利用asm volatile ("lfence;rdtsc;lfence" : "=a" (a), "=d" (d)::"memory");
如果您的 CPU 可能会在运行时重新排序指令。这里volatile意味着不需要mfence
around cflush
为了保证
之后的指示cflush
可以观察其效果)
#include <stdio.h>
#include <stdint.h>
inline void
clflush(volatile void *p)
{
asm volatile ("clflush (%0)" :: "r"(p));
}
inline uint64_t
rdtsc()
{
unsigned long a, d;
asm volatile ("rdtsc" : "=a" (a), "=d" (d));
return a | ((uint64_t)d << 32);
}
volatile int i;
inline void
test()
{
uint64_t start, end;
volatile int j;
start = rdtsc();
j = i;
end = rdtsc();
printf("took %lu ticks\n", end - start);
}
int
main(int ac, char **av)
{
test();
test();
printf("flush: ");
clflush(&i);
test();
test();
return 0;
}