3

我正在尝试用 C 编写一个微型基准测试来测试内存。

我相信我的机器(Intel i5)上的缓存大小是 8MB..

有人可以建议一些逻辑来测试内存,同时确保 100% 的缓存未命中率吗?

array1 = malloc(DCACHE_SIZE);
array2 = malloc(DCACHE_SIZE);
while(condition)
    memcpy(&array1[index], &array2[index], sizeof(char));
    index++;

目前,使用 memcpy,我的程序每秒对 memcpy 调用 420,782,149 次。我认为这个数字有严重问题(它击中缓存很多)

如何避免缓存?

4

4 回答 4

1

如上所述禁用缓存非常复杂,相反,您可以使用完全避免它们的数据操作方法。

最好的方法是定义一个不可缓存的内存区域,这样每次读/写都会立即进入内存并跳过填充缓存,但这也需要在更高级的级别调整您的程序。

我能想到的最简单的解决方案是直接使用跳过缓存的流/非临时指令 - 如果编译器识别它们,请尝试 _mm_stream_si64 / _mm_stream_si32 内在函数,或者直接在内联汇编部分中使用 movnt* 汇编指令系列 - 它应该对您的处理器具有几乎相同的效果。请注意,它们操作的元素大于单个字节,因此您可能需要重新排列代码

于 2013-08-10T10:29:29.287 回答
1

我对 Intel i5 缓存架构并不特别熟悉,但有两种基本方法应该适用于大多数处理器:

  1. 禁用内存缓冲区的 L1/L2/L3 缓存。这可能是确保不使用缓存的唯一真正方法。一种变体是将其他一些未使用的内存区域的内容锁定到缓存中(即,如果禁用不是一个选项)。
  2. 如果第一种方法不是一个选项,请使您的阵列远大于您的 DCACHE 大小并memcpy()超过该区域。这里的想法是缓存将被使用,但将在大型阵列的新部分被拉入缓存时被刷新。这应该会提供一个非常接近于直接从 CPU 到内存的基准。如果您使用memset()而不是memcpy() 并且您的缓存是直写的,则此基准测试应该与直接 CPU 到内存路径相同。

在这两种情况下,为了获得更精确的结果,您应该在开始测试之前确保缓存中的内容array1[]array2[]存在。这可能需要在memcpy()测试之前分配和填充(或简单地读取)第三个缓冲区。当试图避免缓存时,有许多这些类型的陷阱,以及如何解决和避免它们特定于缓存体系结构以及您的操作系统如何配置缓存(即,如果它是 Linux,默认情况下它可能不会) t 将缓存配置为直写)。

顺便说一句,您是否意识到您正在使用您的memcpy()方法测试内存读取和写入?这种方法很好,但可能会产生更不可靠的结果。更好的方法可能是分别测试读取和写入,而不是使用memset()memcpy().

于 2013-06-03T20:41:18.143 回答
1

强制缓存未命中的一种简单方法是在保证位于不同缓存窗口中的区域之间跳转,例如:

#include <string.h>
#define DCACHE_SIZE (1024*1024*8)

void dummy(){
char *array1, *array2;
size_t index, count;

array1 = malloc(5*DCACHE_SIZE);
array2 = malloc(5*DCACHE_SIZE);
for(index=0,count=54321;count--; index = (index+3) % (5*DCACHE_SIZE)) {
    memcpy(&array1[index], &array2[index], 1);
    }
}

35上面是任意选择的(但应该是互质的);1并且2也足以在每次迭代时跳出缓存。另请注意,memcpy() 的源和目标也在不同的缓存槽中,因此如果缓存槽少于 2 个,此代码还会在循环的每次迭代中导致两次缓存未命中。顺便说一句:在我的机器上,GCC 用内联指令替换了 memcpy() 调用。

于 2013-08-10T11:23:39.223 回答
0

如果您不禁用缓存,我也会关闭预取。
此外,至少循环运行 10 次测试并记录结果。
在 for 循环中销毁并重新创建数组,看看如果您只是在 for 循环之前分配数组,则时间有何不同。

在您的 420M 结果中:大约是 420 MB/s 复制(读取和写入)。根据您的 RAM 速度,这似乎是一个低数字。
您还可以查看弗吉尼亚大学的 Stream 基准进行比较。

于 2013-08-09T22:27:59.950 回答