【发布时间】:2011-05-14 17:17:58
【问题描述】:
总结:
memcpy 在我的系统上的真实或测试应用程序中似乎无法以超过 2GB/秒的速度传输。如何获得更快的内存到内存副本?
详细信息:
作为数据捕获应用程序的一部分(使用一些专用硬件),我需要将大约 3 GB/秒的速度从临时缓冲区复制到主内存中。为了获取数据,我为硬件驱动程序提供了一系列缓冲区(每个 2MB)。硬件 DMA 将数据发送到每个缓冲区,然后在每个缓冲区已满时通知我的程序。我的程序清空缓冲区(memcpy 到另一个更大的 RAM 块),并将处理后的缓冲区重新发布到卡上以再次填充。我在使用 memcpy 足够快地移动数据时遇到问题。似乎内存到内存的复制速度应该足够快,可以在我正在运行的硬件上支持 3GB/秒。 Lavalys EVEREST 为我提供了 9337MB/秒的内存复制基准测试结果,但我无法使用 memcpy 达到这样的速度,即使在一个简单的测试程序中也是如此。
我已通过在缓冲区处理代码中添加/删除 memcpy 调用来隔离性能问题。如果没有 memcpy,我可以运行完整的数据速率——大约 3GB/秒。启用 memcpy 后,我的速度被限制在大约 550Mb/秒(使用当前编译器)。
为了在我的系统上对 memcpy 进行基准测试,我编写了一个单独的测试程序,它只对某些数据块调用 memcpy。 (我在下面发布了代码)我已经在我正在使用的编译器/IDE(National Instruments CVI)以及 Visual Studio 2010 中运行了它。虽然我目前没有使用 Visual Studio,但我愿意如果它会产生必要的性能,则进行切换。但是,在盲目地移动之前,我想确保它能够解决我的 memcpy 性能问题。
Visual C++ 2010:1900 MB/秒
NI CVI 2009:550 MB/秒
虽然 CVI 明显比 Visual Studio 慢我并不感到惊讶,但我对 memcpy 性能如此之低感到惊讶。虽然我不确定这是否可以直接比较,但这远低于 EVEREST 基准带宽。虽然我不需要那么高的性能,但至少需要 3GB/秒。标准库的实现肯定不会比 EVEREST 使用的差这么多!
如果有的话,在这种情况下我可以做些什么来让 memcpy 更快?
硬件细节: AMD Magny Cours - 4x 八核 128GB DDR3 Windows Server 2003 企业版 X64
测试程序:
#include <windows.h>
#include <stdio.h>
const size_t NUM_ELEMENTS = 2*1024 * 1024;
const size_t ITERATIONS = 10000;
int main (int argc, char *argv[])
{
LARGE_INTEGER start, stop, frequency;
QueryPerformanceFrequency(&frequency);
unsigned short * src = (unsigned short *) malloc(sizeof(unsigned short) * NUM_ELEMENTS);
unsigned short * dest = (unsigned short *) malloc(sizeof(unsigned short) * NUM_ELEMENTS);
for(int ctr = 0; ctr < NUM_ELEMENTS; ctr++)
{
src[ctr] = rand();
}
QueryPerformanceCounter(&start);
for(int iter = 0; iter < ITERATIONS; iter++)
memcpy(dest, src, NUM_ELEMENTS * sizeof(unsigned short));
QueryPerformanceCounter(&stop);
__int64 duration = stop.QuadPart - start.QuadPart;
double duration_d = (double)duration / (double) frequency.QuadPart;
double bytes_sec = (ITERATIONS * (NUM_ELEMENTS/1024/1024) * sizeof(unsigned short)) / duration_d;
printf("Duration: %.5lfs for %d iterations, %.3lfMB/sec\n", duration_d, ITERATIONS, bytes_sec);
free(src);
free(dest);
getchar();
return 0;
}
编辑:如果您有额外的 5 分钟时间并想做出贡献,您可以在您的机器上运行上述代码并将您的时间作为评论发布吗?
【问题讨论】:
-
我的笔记本显示相同的内存带宽。但是一个快速设计的 sse2/4 算法并没有提高性能(只有一点点)。
-
使用 SSE 代码进行更多测试只会使 VC2010 中的 memcpy 算法的速度提高 60 MB/秒。 Core-i5 笔记本电脑的峰值约为 2,224 GB/秒(这个数字不应该翻倍吗?我们正在写入这个数字并同时读取它,所以 ~4,4 GB/秒......)。要么可以做一些我忽略的事情,要么你真的必须“不复制”你的数据。
-
查看 onemasse 的答案(William Chan 的 memcpy 的 SSE2 ASM 实现) - 使用 memcpy 和 CopyMemory,我得到 1.8GB/s。通过 William 的实现,我得到了 3.54GB/s(几乎翻了一番!)。这是在 Core2Duo wolfdale 上,带有 2 通道 DDR2,频率为 800MHz。
-
在我下面的回答中,我刚刚想到从采集卡传输数据会消耗一些 CPU 可用的内存带宽,我想你会损失大约 33% (memcpy = 读/写,带采集卡 = 写/读/写),因此您的应用内 memcpy 将比基准 memcpy 慢。
-
Macbook Retina Pro Core,i7 2.6GHz(Win 7 x64 via Bootcamp):8474 MB/秒。编译器是 Embarcadero C++Builder 2010
标签: c visual-studio memcpy cvi memory-bandwidth