您需要想出一种启发式方法,以强制实现100%(或非常接近)的缓存未命中(希望您有一个缓存失效操作代码?),并且实现100%缓存命中。很好,这对第一层缓存可以起作用。现在,如何在二级和三级缓存中做到这一点?说真的,可能没有一种方法可以百分之百地可靠地完成这个任务,除非使用特殊硬件和连接到CPU和内存的跟踪器。但是这是我会做的:在内存中的一个位置写入足够多的数据,确保它始终命中L1缓存,并记录时间(这会影响您的缓存,所以要小心)。您应该在无需分支的情况下进行此组写入,以尽可能消除分支预测不一致性。这是最佳时间。现在,不时地向远离已知位置右侧的随机位置RAM中写入一个缓存行大小的数据,并记录新时间。希望这需要更长时间。不断重复此操作,记录各种时间,希望您能看到一些时间会聚成几组。每个组“可能”显示L2、L3和内存访问时间的时间。问题在于其他许多东西都会妨碍此过程。操作系统可能会切换上下文并破坏您的缓存。中断可能会来了,打乱您的计时。有许多东西可能会使值失真。但是,希望您在数据中获得足够的信号以查看它是否有效。这在较简单的嵌入式系统上可能更容易完成,因为操作系统(如果有)不会妨碍您的工作。
这通常需要对缓存的“几何”和其他方面有一定了解。此外,拥有一些系统控制能力也很有帮助,超出简单用户访问它和比标准C时钟机制提供更精细时间等实现相关事物。以下是一个初始方法:编写一个例程,需要指向内存的指针、长度和重复次数,并按顺序读取所有的内存,重复这个过程。编写一个例程,需要指向内存的指针、长度和重复次数,并按顺序将所有的内存写入,重复这个过程。上述例程可能需要将它们的指针转换为易失性指针,以防止编译器优化掉没有效果的访问。分配大量内存。调用上述每个例程,在每次调用之前和之后获取当前时间,并使用各种长度调用,以查看不同长度的时间。这样做,你通常会看到小长度的快速速度(每秒读/写字节数),而更长长度的速度较慢。当超过不同级别缓存的大小时,速度下降会发生。因此,使用上述技术收集的数据中很可能会反映L1和L2缓存的大小。以下是该方法不足的一些原因:它不能控制用于读取或写入缓存的指令。C编译器可能会生成load-word和store-word指令,但许多现代处理器具有可以一次加载和存储16个字节的指令,并且使用这些指令读取和写入可能比使用四个字节的word指令更快。当您按顺序访问时,缓存的行为将与随机访问时不同。大多数缓存都会尝试跟踪数据何时被使用,以便最近使用的数据保留在缓存中,而其他数据则被弃用。实际程序的访问部分通常与上述连续操作不同。特别是,对内存的连续写入可能能够填充整个缓存行,因此不需要从内存读取任何内容,而只写入一个单词到特定位置的实际使用模式可能需要从内存读取缓存行并合并更改的字节。来自系统中其他进程的竞争将干扰缓存内容和测量。
请看cachegrind-valgrind: Cachegrind 模拟程序与机器缓存层次结构(可选支持分支预测)的交互。它模拟了一台具有独立的一级指令和数据缓存(I1 和 D1),由统一的二级缓存(L2)支持的机器。这恰好符合许多现代机器的配置。 请看以下相关问题: 如何以编程方式禁用硬件预取? 如何从用户模式代码中通用地检测高速缓存行关联性? 如何在基准测试时使缓存无效?