非常快速的图像处理memcpy?

hor*_*guy 32 c optimization assembly image-processing memcpy

我在C中进行图像处理,需要在内存周围复制大块数据 - 源和目标永远不会重叠.

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

我希望解决方案可以是汇编还是使用GCC内在函数?

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

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

小智 40

William Chan和Google提供.比Microsoft Visual Studio 2005中的memcpy快30-70%.

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:
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以根据您的具体情况和您能够做出的任何假设进一步优化它.

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

  • 注意:此memcopy的性能将极大地取决于要复制的数据量和缓存大小.例如,与常规movdqa相比,预取和非时间移动可能会使较小(适合L2)副本的性能陷入困境. (6认同)
  • 我记得先在AMD64手册中阅读此代码.并且代码在intel上不是最优的,它具有缓存库别名问题. (3认同)
  • 栏杆:别忘了给他发邮件说你在项目中使用了他的代码;)[http://williamchan.ca/portfolio/assembly/ssememcpy/source/viewsource.php?id=readme.txt] (2认同)

caf*_*caf 6

在任何优化级别-O1或更高级别,GCC将对函数使用内置定义memcpy- 使用正确的-march参数(-march=pentium4对于您提到的功能集),它应该生成非常优化的体系结构特定的内联代码.

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


Nil*_*nck 6

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

如果你需要更高的性能并且不要回避编写设备驱动程序的漫长而曲折的道路:现在所有重要的平台都有一个DMA控制器,能够更快地执行复制作业并与CPU代码并行能做.

这涉及到写一个驱动程序.由于存在安全风险,我所知道的大型操作系统都没有向用户端公开此功能.

但是,它可能是值得的(如果你需要性能),因为地球上没有任何代码可以胜过设计用于完成这项工作的硬件.

  • 我刚刚发布了一个关于 RAM 带宽的答案。如果我说的是真的,那么我不认为 DMA 引擎可以实现超出 CPU 的功能。我错过了什么吗? (2认同)

And*_*dge 6

这个问题现在已经有四年了,我有点惊讶没人提到内存带宽.CPU-Z报告我的机器有PC3-10700 RAM.RAM具有10700 MBytes/sec的峰值带宽(也称为传输速率,吞吐量等).我机器中的CPU是i5-2430M CPU,峰值turbo频率为3 GHz.

从理论上讲,CPU和RAM的速度无限快,memcpy可以达到5300 MB /秒,即10700的一半,因为memcpy必须读取然后写入RAM.(编辑:正如v.oddou所指出的,这是一个简单的近似).

另一方面,想象一下我们拥有无限快速的RAM和逼真的CPU,我们可以实现什么?我们以3 GHz CPU为例.如果它可以在每个周期进行32位读取和32位写入,那么它可以传输3e9*4 = 12000 MBytes/sec.对于现代CPU而言,这似乎很容易实现.我们已经可以看到,CPU上运行的代码并不是真正的瓶颈.这是现代机器具有数据缓存的原因之一.

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

  • 40 KByte阵列:16000 MBytes/sec
  • 400 KByte阵列:11000 MBytes/sec
  • 4000 KByte阵列:3100 MBytes/sec

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

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

总之,在CPU上运行的代码不是瓶颈,调整代码只会做一些小的改进.