我正在用C语言做图像处理,这需要在内存中复制大量数据-源和目标永远不会重叠。
在x86平台上使用GCC(其中SSE、SSE2但不包括SSE3)执行此操作的绝对最快方法是什么?
我希望解决方案将是在汇编或使用GCC内部?
我找到了下面的链接,但不知道这是否是最好的方法(作者也说它有一些bug):http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2006-02/msg00123.html
编辑:注意,复制是必要的,我不能绕过必须复制数据(我可以解释为什么,但我会省去你的解释:))
8条答案
按热度按时间sbtkgmzw1#
来自William Chan和Google。比Microsoft Visual Studio 2005中的memcpy快30-70%。
您可以根据您的具体情况和您能够做出的任何假设进一步优化它。
您可能还想查看memcpy源代码(memcpy.asm)并去掉其特殊情况处理。可以进一步优化!
h6my8fg22#
hapalibashi发布的SSE代码是要走的路。
如果您需要更高的性能,并且不回避编写设备驱动程序的漫长而曲折的道路:现在所有重要的平台都有一个DMA控制器,它能够更快地完成复制工作,并且与CPU代码并行。
这就需要写一个驱动程序。据我所知,没有大型操作系统会因为安全风险而向用户端公开此功能。
然而,这可能是值得的(如果您需要性能),因为地球上没有任何代码可以胜过设计用于完成此类工作的硬件。
o8x7eapl3#
这个问题已经有四年了,我有点惊讶还没有人提到内存带宽。CPU-Z报告我的机器有PC 3 -10700 RAM。RAM的峰值带宽(也称为传输速率、吞吐量等)为10700 MB/秒。我的机器中的CPU是i5- 2430 M CPU,峰值turbo频率为3 GHz。
理论上,有了无限快的CPU和我的RAM,memcpy可以达到5300 MB/秒,即10700的一半,因为memcpy必须从RAM读取,然后写入RAM。(编辑:正如v.oddou指出的,这是一个过于简单的近似)。
另一方面,想象我们有无限快的RAM和一个现实的CPU,我们能实现什么?让我们以我的3GHz CPU为例。如果它可以在每个周期进行32位读取和32位写入,则它可以传输3e 9 * 4 =12000 MB/秒。这似乎很容易达到一个现代的CPU。我们已经可以看到,在CPU上运行的代码并不是真正的瓶颈。这是现代机器具有数据缓存的原因之一。
当我们知道数据被缓存时,我们可以通过对memcpy进行基准测试来衡量CPU真正能做什么。准确地做到这一点是很麻烦的。我做了一个简单的应用程序,将随机数写入一个数组,将它们memcpy到另一个数组,然后对复制的数据进行校验和。我在调试器中逐步检查了代码,以确保聪明的编译器没有删除副本。更改阵列大小会改变该高速缓存性能-小阵列适合缓存,大阵列则不适合。我得到了以下结果:
显然,我的CPU每个周期可以读写超过32位,因为16000比我上面理论计算的12000多。这意味着CPU的瓶颈比我想象的要小。我使用的是VisualStudio 2005,进入标准的memcpy实现,我可以看到它在我的机器上使用了movqda指令。我猜这可以读和写64位每周期。
hapalibashi发布的代码在我的机器上达到了4200 MB/秒-比VS 2005实现快了40%。我猜它更快,因为它使用预取指令来提高缓存性能。
总之,在CPU上运行的代码不是瓶颈,对代码进行调优只会带来很小的改进。
xqnpmsa84#
在
-O1
或更高的任何优化级别上,GCC将使用memcpy
等函数的内置定义-使用正确的-march
参数(-march=pentium4
用于您提到的功能集),它应该生成非常优化的特定于体系结构的内联代码。我会做个基准测试看看结果如何。
7vhp5slm5#
如果是针对英特尔处理器,您可能会受益于IPP。如果你知道它将在Nvidia GPU上运行,也许你可以使用CUDA--在这两种情况下,看起来比优化memcpy()更广泛--它们为在更高层次上改进你的算法提供了机会。然而,它们都依赖于特定的硬件。
ef1yzkbh6#
如果您使用的是Windows,请使用DirectX API,该API具有特定的GPU优化例程,用于图形处理(它能有多快?您的CPU未加载。在GPU咀嚼它的同时做其他事情)。
如果你想成为操作系统不可知论者,试试OpenGL。
不要摆弄汇编程序,因为你很可能会惨败,无法超过10年以上熟练的库制作软件工程师。
6vl6ewon7#
老问题,但有两件事至今没有人指出:
1.大多数编译器都有自己的
memcpy
版本;由于memcpy
是定义良好的,也是C标准的一部分,编译器不必使用系统库附带的实现,他们可以自由使用自己的实现。正如问题中提到的“intrinsic”,实际上大多数时候你在代码中编写memcpy
,实际上你使用的是编译器内部函数,因为这是编译器内部使用的,而不是真实的调用memcpy
,因为它甚至可以内联它,从而消除任何函数调用开销。1.我所知道的大多数
memcpy
实现在可用时都在内部使用了SSE 2之类的东西,至少好的实现是这样的。VisualStudio 2005中的一个可能没有使用它,但GCC已经使用了很长时间。当然,它们使用什么取决于构建设置。它们将只使用代码将在其上运行的所有CPU可用的指令,因此请确保正确设置架构(例如march
和mtune
),以及其他标志(例如使得能够支持可选的指令集)。所有这些都会影响编译器在最终二进制文件中为memcpy
生成的代码。所以,不要以为你可以胜过编译器或系统(不同的CPU可能有不同的
memcpy
实现),基准测试可以证明这一点!除非一个基准测试表明你的手写代码在真实的生活中更快,否则就把它留给编译器和系统,因为它们会适应新的CPU,系统可能会得到更新,自动使你的代码在未来运行得更快,而你必须自己重新优化手写代码,除非你自己发布更新,否则它永远不会变得更快。6qfn3psc8#
如果您可以访问DMA引擎,没有什么会更快。