图像处理速度非常快吗?
c
image-processing
optimization
4
0

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

在使用GCC (可以使用SSE ,SSE2但不提供SSE3的平台)上的x86平台上,这样做的绝对最快方法是什么?

我希望解决方案将是在组装中还是使用GCC内部函数?

我找到了以下链接,但不知道它是否是解决该问题的最佳方法(作者还说它存在一些错误): http : //coding.derkeiler.com/Archive/Assembler/comp.lang.asm。 x86 / 2006-02 / msg00123.html

编辑:请注意,有必要进行复制,我无法避免必须复制数据(我可以解释原因,但我将为您省去解释:))

参考资料:
Stack Overflow
收藏
评论
共 6 个回答
高赞 时间 活跃

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

我对它进行了基准测试,看看结果如何。

收藏
评论

这个问题现在已经四岁了,令我惊讶的是,没有人提到内存带宽。 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 MBytes / sec 。对于现代CPU而言,这似乎很容易实现。我们已经可以看到,CPU上运行的代码并不是真正的瓶颈。这是现代机器具有数据缓存的原因之一。

当我们知道数据已缓存时,我们可以通过对memcpy进行基准测试来衡量CPU的实际功能。准确地做到这一点很奇怪。我制作了一个简单的应用程序,将随机数写入一个数组,将它们存储到另一个数组,然后对复制的数据进行校验和。我逐步调试了调试器中的代码,以确保聪明的编译器没有删除副本。更改数组的大小会更改缓存性能-小型数组适合缓存,大数组则适合。我得到以下结果:

  • 40 KB数组:16000 MB /秒
  • 400 KB数组:11000 MB /秒
  • 4000 KB数组:3100 MB /秒

显然,我的CPU每个周期可读写32位以上,因为16000大于我理论上计算出的12000。这意味着CPU甚至比我已经想到的要少的瓶颈。我使用了Visual Studio 2005,并进入了标准的memcpy实现,可以看到它在我的机器上使用movqda指令。我猜这每个周期可以读写64位。

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

总而言之,CPU上运行的代码不是瓶颈,调整代码只会带来很小的改进。

收藏
评论

感谢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:
  }
}

您可能可以根据自己的实际情况和能够做出的任何假设来进一步优化它。

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

收藏
评论

由hapalibashi发布的SSE代码是必经之路。

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

但这涉及编写驱动程序。我知道,没有任何大型操作系统会因为安全风险而将此功能公开给用户。

但是,这可能是值得的(如果您需要性能),因为地球上没有任何代码可以胜过旨在完成此任务的硬件。

收藏
评论

如果您使用的是Windows,请使用DirectX API,该API具有经过GPU优化的特定例程以进行图形处理(速度有多快?您的CPU尚未加载。在GPU对其进行压缩时,请执行其他操作)。

如果您想与操作系统无关,请尝试使用OpenGL

不要摆弄汇编程序,因为您极有可能在性能上超过10年以上熟练的库制作软件工程师而惨败。

收藏
评论

如果特定于Intel处理器,则可以从IPP中受益。如果您知道它将与Nvidia GPU一起运行,那么您可以使用CUDA-在这两种情况下,看起来宽阔都比优化memcpy()更好-它们为更高级别的算法提供了机会。但是,它们都依赖于特定的硬件。

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号