2009-11-11 230 views
29

我在做C中的图像处理,需要在内存周围复制大块数据 - 源和目标不会重叠。非常快速的图像处理memcpy?

什么是在x86平台上使用GCC(其中SSE,SSE2而不是SSE3可用)执行此操作的最快速方式?

我希望解决方案可以是装配或使用GCC内部函数?

我发现下面的链接,但不知道是否这是最好的方式去它(笔者也表示有一些错误):http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2006-02/msg00123.html

编辑:注意,副本是必要的,我不能避开不必复制数据(我可以解释为什么,但我会饶你的解释:))

+0

你能写你的代码,所以副本不是必需的吗? – Ron 2009-11-11 13:44:27

+0

Ron,不,我不能:( – horseyguy 2009-11-11 13:47:32

+1

如果你能得到英特尔编译器的保留,你可能有更好的机会将优化器转换成矢量cpu指令 – 2009-11-11 13:54:05

回答

38

致谢William Chan和Google。比快的memcpy在Microsoft Visual Studio 2005

void X_aligned_memcpy_sse2(void* dest, const void* src, const unsigned long size) 
{ 

    __asm 
    { 
    mov esi, src; //src pointer 
    mov edi, dest; //dest pointer 

    mov ebx, size; //ebx is our counter 
    shr ebx, 7;  //divide by 128 (8 * 128bit registers) 


    loop_copy: 
     prefetchnta 128[ESI]; //SSE2 prefetch 
     prefetchnta 160[ESI]; 
     prefetchnta 192[ESI]; 
     prefetchnta 224[ESI]; 

     movdqa xmm0, 0[ESI]; //move data from src to registers 
     movdqa xmm1, 16[ESI]; 
     movdqa xmm2, 32[ESI]; 
     movdqa xmm3, 48[ESI]; 
     movdqa xmm4, 64[ESI]; 
     movdqa xmm5, 80[ESI]; 
     movdqa xmm6, 96[ESI]; 
     movdqa xmm7, 112[ESI]; 

     movntdq 0[EDI], xmm0; //move data from registers to dest 
     movntdq 16[EDI], xmm1; 
     movntdq 32[EDI], xmm2; 
     movntdq 48[EDI], xmm3; 
     movntdq 64[EDI], xmm4; 
     movntdq 80[EDI], xmm5; 
     movntdq 96[EDI], xmm6; 
     movntdq 112[EDI], xmm7; 

     add esi, 128; 
     add edi, 128; 
     dec ebx; 

     jnz loop_copy; //loop please 
    loop_copy_end: 
    } 
} 

您可以去优化它进一步根据您的具体情况,你可以做任何假设30-70%。

您可能还想查看memcpy源(memcpy.asm)并去除其特殊情况处理。有可能进一步优化!

+6

注意:此memcopy的性能将大大取决于要复制的数据量和缓存大小。例如,与普通movdqa相比,预取和非暂时移动可能会使性能下降,以适应较小(适合L2)的副本。 – 2009-11-11 14:39:23

+2

banister:不要忘了给他发邮件说你在你的项目中使用了他的代码;)[http://williamchan.ca/portfolio/assembly/ssememcpy/source/viewsource.php?id=readme.txt] – ardsrk 2009-11-11 14:48:22

+3

我记得先阅读AMD64手册中的这段代码。代码在英特尔上并不是最佳的,它存在缓存银行别名问题。 – hirschhornsalz 2009-11-29 10:33:59

2

如果您使用的是Windows,使用DirectX的API,这对于图形处理特定GPU - 优化程序(它的速度有多快?你的CPU没有被加载,当GPU咀嚼它的时候做一些其他的事情)。

如果您想成为操作系统不可知论者,请尝试OpenGL

不要捣鼓汇编程序,因为它很有可能会惨败于超过10年以上的精通图书馆软件工程师。

+1

更快,我需要它在MEMORY中执行,也就是说,它不会发生在GPU上。 :)另外,我自己并不打算超越库函数(因此为什么我在这里问这个问题),但我确定有人在stackoverflow上,谁可以胜过libs :)另外,库编写者通常是受限制的通过可移植性要求 - 正如我所说我只关心x86平台,所以可能进一步x86特定的优化。 – horseyguy 2009-11-11 14:12:02

+0

+1,因为这是首要的建议 - 即使它不适用于栏杆的情况。 – peterchen 2009-11-12 18:54:03

+1

我不确定这是不错的建议。典型的现代机器具有与CPU和GPU相同的内存带宽。例如,许多流行的笔记本电脑都使用英特尔高清显卡,它使用与CPU相同的内存。 CPU可能已经饱和内存总线。对于memcpy,我希望CPU或GPU具有类似的性能。 – 2013-08-15 11:17:40

3

如果特定于英特尔处理器,则可能从IPP获益。如果你知道它可以使用Nvidia GPU运行,也许你可以使用CUDA - 在这两种情况下,最好比优化memcpy()更宽 - 它们提供了在更高级别上改进算法的机会。但它们都依赖于特定的硬件。

6

截至-O1以上任何优化级别,GCC将使用内置的定义,功能,如memcpy - 用(为你所提到的功能集-march=pentium4)右-march参数应该产生相当的最佳体系结构相关的内嵌代码。

我会对它进行基准测试,看看会发生什么。

6

由hapalibashi发布的SSE代码是要走的路。

如果你需要更多的性能,并且不要回避编写设备驱动程序的漫长而曲折的道路:现在所有重要的平台都有一个DMA控制器,它能够更快地完成复制任务与CPU代码并行可以。

这涉及到编写驱动程序。由于存在安全风险,我不知道如何将这种功能暴露给用户方。

然而,它可能是值得的(如果您需要性能),因为地球上的任何代码都无法超越专为执行此类工作而设计的硬件。

+1

我刚刚发布了一个讨论RAM带宽的答案。如果我说的是真的,那么我不认为DMA引擎可以超越CPU所能达到的效果。我错过了什么吗? – 2013-08-15 11:02:25

5

这个问题现在四岁了,我有点惊讶没有人提到内存带宽。 CPU-Z报告我的机器有PC3-10700 RAM。 RAM的峰值带宽(又称传输速率,吞吐量等)为10700 MBytes/sec。我的机器中的CPU是一个i5-2430M CPU,其峰值turbo频率为3 GHz。理论上讲,使用无限快的CPU和我的RAM,memcpy可以在5300 MBytes/sec,即10700的一半,因为memcpy必须读取然后写入RAM。 (编辑:v.oddou指出,这是一个简单的近似)。另一方面,想象一下,我们拥有无限快速的RAM和逼真的CPU,我们可以实现什么?以我的3 GHz CPU为例。如果每个周期可以执行32位读取和32位写入,则它可以传输3e9 * 4 = 12000兆字节/秒。这似乎很容易达到现代CPU。我们已经可以看到CPU上运行的代码并不是真正的瓶颈。这是现代机器具有数据缓存的原因之一。

当我们知道数据被缓存时,我们可以通过对memcpy进行基准测试来测量CPU的真正功能。准确地做这件事很费劲。我做了一个简单的应用程序,它将随机数写入数组,将它们memcpy到另一个数组,然后检查复制的数据。我浏览了调试器中的代码,以确保聪明的编译器没有删除副本。改变数组的大小会改变缓存的性能 - 小数组适合高速缓存,大数组不太适合。我得到了以下的结果:

  • 40 K字节数组:16000兆字节/秒
  • 400 K字节的数组:11000兆字节/秒
  • 4000 K字节阵列:3100兆字节/秒

显然,我的CPU每个周期可以读写32位以上,因为16000比我在理论上计算的12000多。这意味着CPU比我想象的更不是瓶颈。我使用Visual Studio 2005,并进入标准的memcpy实现,我可以看到它在我的机器上使用了movqda指令。我想这可以读写每个周期64位。

在我的机器上发布的漂亮代码hapalibashi达到了4200 MBytes /秒 - 比VS 2005实现速度快大约40%。我猜它更快,因为它使用预取指令来提高缓存性能。

总之,在CPU上运行的代码不是瓶颈,并且调优代码只会做出很小的改进。

+0

你的思考过程很好。然而,你没有考虑RAM的市场推广数量,这是所有四通道抽取的数字,与1通道的速度并不相符。而且这也是公交前的速度,核心i7/opterons在numa模型中也存在管理费用。 – 2013-10-30 08:03:05