非常快速的图像处理memcpy?
我在做C中的图像处理,需要在内存周围复制大块数据 - 源和目标不会重叠。非常快速的图像处理memcpy?
什么是在x86平台上使用GCC(其中SSE,SSE2而不是SSE3可用)执行此操作的最快速方式?
我希望解决方案可以是装配或使用GCC内部函数?
我发现下面的链接,但不知道是否这是最好的方式去它(笔者也表示有一些错误):http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2006-02/msg00123.html
编辑:注意,副本是必要的,我不能避开不必复制数据(我可以解释为什么,但我会饶你的解释:))
致谢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)并去除其特殊情况处理。有可能进一步优化!
注意:此memcopy的性能将大大取决于要复制的数据量和缓存大小。例如,与普通movdqa相比,预取和非暂时移动可能会使性能下降,以适应较小(适合L2)的副本。 – 2009-11-11 14:39:23
banister:不要忘了给他发邮件说你在你的项目中使用了他的代码;)[http://williamchan.ca/portfolio/assembly/ssememcpy/source/viewsource.php?id=readme.txt] – ardsrk 2009-11-11 14:48:22
我记得先阅读AMD64手册中的这段代码。代码在英特尔上并不是最佳的,它存在缓存银行别名问题。 – hirschhornsalz 2009-11-29 10:33:59
如果您使用的是Windows,使用DirectX的API,这对于图形处理特定GPU - 优化程序(它的速度有多快?你的CPU没有被加载,当GPU咀嚼它的时候做一些其他的事情)。
如果您想成为操作系统不可知论者,请尝试OpenGL。
不要捣鼓汇编程序,因为它很有可能会惨败于超过10年以上的精通图书馆软件工程师。
更快,我需要它在MEMORY中执行,也就是说,它不会发生在GPU上。 :)另外,我自己并不打算超越库函数(因此为什么我在这里问这个问题),但我确定有人在stackoverflow上,谁可以胜过libs :)另外,库编写者通常是受限制的通过可移植性要求 - 正如我所说我只关心x86平台,所以可能进一步x86特定的优化。 – horseyguy 2009-11-11 14:12:02
+1,因为这是首要的建议 - 即使它不适用于栏杆的情况。 – peterchen 2009-11-12 18:54:03
我不确定这是不错的建议。典型的现代机器具有与CPU和GPU相同的内存带宽。例如,许多流行的笔记本电脑都使用英特尔高清显卡,它使用与CPU相同的内存。 CPU可能已经饱和内存总线。对于memcpy,我希望CPU或GPU具有类似的性能。 – 2013-08-15 11:17:40
截至-O1
以上任何优化级别,GCC将使用内置的定义,功能,如memcpy
- 用(为你所提到的功能集-march=pentium4
)右-march
参数应该产生相当的最佳体系结构相关的内嵌代码。
我会对它进行基准测试,看看会发生什么。
由hapalibashi发布的SSE代码是要走的路。
如果你需要更多的性能,并且不要回避编写设备驱动程序的漫长而曲折的道路:现在所有重要的平台都有一个DMA控制器,它能够更快地完成复制任务与CPU代码并行可以。
这涉及到编写驱动程序。由于存在安全风险,我不知道如何将这种功能暴露给用户方。
然而,它可能是值得的(如果您需要性能),因为地球上的任何代码都无法超越专为执行此类工作而设计的硬件。
我刚刚发布了一个讨论RAM带宽的答案。如果我说的是真的,那么我不认为DMA引擎可以超越CPU所能达到的效果。我错过了什么吗? – 2013-08-15 11:02:25
这个问题现在四岁了,我有点惊讶没有人提到内存带宽。 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上运行的代码不是瓶颈,并且调优代码只会做出很小的改进。
你的思考过程很好。然而,你没有考虑RAM的市场推广数量,这是所有四通道抽取的数字,与1通道的速度并不相符。而且这也是公交前的速度,核心i7/opterons在numa模型中也存在管理费用。 – 2013-10-30 08:03:05
你能写你的代码,所以副本不是必需的吗? – Ron 2009-11-11 13:44:27
Ron,不,我不能:( – horseyguy 2009-11-11 13:47:32
如果你能得到英特尔编译器的保留,你可能有更好的机会将优化器转换成矢量cpu指令 – 2009-11-11 13:54:05