C:动态分配数组的memcpy速度

我需要有关以下代码性能的帮助。 它对两个任意大小的动态分配数组执行memcpy:

int main() { double *a, *b; unsigned n = 10000000, i; a = malloc(n*sizeof(double)); b = malloc(n*sizeof(double)); for(i=0; i<n; i++) { a[i] = 1.0; /* b[i] = 0.0; */ } tic(); bzero(b, n*sizeof(double)); toc("bzero1"); tic(); bzero(b, n*sizeof(double)); toc("bzero2"); tic(); memcpy(b, a, n*sizeof(double)); toc("memcpy"); } 

tic / toc测量执行时间。

在我的电脑上,memcpy需要0.035秒(Linux,gcc版本4.4.6)。 如果我现在取消注释初始化目标数组b的行,则代码快三倍(!) – 0.011s。

我在使用循环而不是memcpy时观察到类似的行为。 通常我不关心这个,因为它足以在使用之前“初始化”内存。 但是,我现在需要执行一个简单的内存复制,并尽可能快地完成。 初始化数据需要将0写入存储器,这不是必需的并且需要时间。 我想用所有可用的内存带宽执行内存复制。

这个问题有解决方案吗? 或者它是否与Linux处理动态内存(某种惰性页面分配?)的方式相关联,并且无法解决? 它在其他系统上怎么样?

编辑:使用gcc 4.6获得相同的结果。 我用-O3编译。

编辑:谢谢大家的意见。 我知道内存映射需要时间。 我想我只是很难接受它需要这么长的时间,比实际的内存访问时间长得多。 代码已被修改为包含使用两个后续bzero调用的数组b初始化的基准。 时间现在显示

bzero1 0.273981
bzero2 0.056803
memcpy 0.117934

显然,第一个bzero调用不仅仅是将零流传输到内存 – 即内存映射和内存清零。 另一方面,第二个bzero调用占用了memcpy所需的一半时间,这与预期完全一样 – 只写时间与读写时间。 据我所知,由于操作系统安全原因,第二次bzero调用的开销必须在那里。 剩下的呢? 我不能以某种方式减少它,例如使用更大的内存页面吗? 不同的内核设置?

我应该提一下,我在Ubuntu wheeze上运行它。

由于(1)延迟页面分配和(2)内核的惰性页面零初始化,第一个bzero运行时间更长。 虽然由于安全原因第二个原因是不可避免的,但是可以通过使用更大(“巨大”)的页面来优化延迟页面分配。

至少有两种方法可以在Linux上使用大页面。 艰难的方式是hugetlbfs 。 简单的方法是透明的巨大页面 。

在系统上的进程列表中搜索khugepaged 。 如果存在这样的过程,则支持透明的大页面,如果将malloc更改为此,则可以在应用程序中使用它们:

 posix_memalign((void **)&b, 2*1024*1024, n*sizeof(double)); madvise((void *)b, n*sizeof(double), MADV_HUGEPAGE); 

它可能是懒惰的页面分配,Linux只在第一次访问时映射页面。 IIRC在Linux的新块中的每个页面都是一个空白页面的写入时复制,并且您的分配足以要求新块。

如果你想解决它,你可以用4k的间隔写一个字节或一个字。 这可能会使映射到RAM的虚拟地址比写入整个页面的速度快一些。

我不希望(最有效的解决办法强制执行延迟内存映射)加上(复制)要比没有初始化b (复制)明显更快,但是。 因此,除非有一个特定的原因让你关心副本的性能,而不是整个操作的性能,否则它是徒劳的。 这是“立即付款或稍后付款”,Linux稍后支付,而您只是为以后测量时间。

当然,如果您将初始化速度和复制速度与仅复制速度进行比较,则初始化应包含在定时部分中。 在我看来你应该实际比较这个:

 // Version 1 for(i=0; i 

对此:

 // Version 2 for(i=0; i 

我预计这将使你的3倍速度提升急剧下降。

编辑:正如Steve Jessop所建议的那样,您可能还想测试第三种策略,即每页只触摸一个条目:

 // Version 3 for(i=0; i