记忆的微观基准

时间:2013-06-03 20:10:26

标签: c benchmarking microbenchmark

我试图用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 个答案:

答案 0 :(得分:1)

我不熟悉英特尔i5缓存架构,但有两种基本方法适用于大多数处理器:

  1. 禁用内存缓冲区的L1 / L2 / L3缓存。这可能是确保不使用缓存的唯一真正方法。这种情况的一种变体是将一些其他未使用的内存区域的内容锁定到缓存中(即,如果禁用禁用的话)。
  2. 如果第一种方法不是一个选项,请使您的数组远大于您的DCACHE大小,并使该区域memcpy()大于memset()。这里的想法是将使用缓存,但是当大数组的新部分被拉入缓存时将被刷新。这应该给出一个非常接近于直接从CPU到内存的基准。如果您使用memcpy()而非array1[] ,则您的缓存是直写式,此基准测试应与直接的CPU到内存路径相同。
  3. 在这两种情况下,为了获得更精确的结果,您应该确保在开始测试之前array2[]memcpy()的内容尚未在缓存中。这可能需要在memcpy()测试之前分配和填充(或简单地读取)第三个缓冲区。当试图避免缓存以及如何解决并避免它们时,有许多这类陷阱是特定于缓存架构以及如何通过操作系统配置缓存(即,如果它是Linux,默认情况下它可能赢了&# 39;将缓存配置为直写)。

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分: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是任意选择的(但应该是相对素数); 12也足以在每次迭代时跳出缓存。另请注意,memcpy()的源和目标也位于不同的缓存槽中,因此只有少于2个缓存槽,此代码也会在循环的每次迭代中导致两次缓存未命中。顺便说一句:在我的机器上,GCC用内联指令替换了memcpy()调用。

答案 3 :(得分:0)

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

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