为什么memmove比memcpy快?

为什么memmove比memcpy快?

我正在调查一个应用程序中的性能热点,该应用程序在memmove(3)中花费了50%的时间。应用程序将数百万个4字节的整数插入到排序数组中,并使用memmove将数据“向右”移位,以便为插入的值腾出空间。

我的期望是复制内存是非常快的,我很惊讶在memmove上花了这么多时间。但是后来我有了一个想法,memmove之所以很慢,是因为它移动重叠的区域,这必须在一个紧密的循环中实现,而不是复制大量的内存页面。我写了一个小的微基准测试,看看memcpy和memmove之间是否存在性能差异,希望memcpy能轻松取胜。

我在两台机器(核心i5,核心i7)上运行我的基准测试,发现memmove实际上比memcpy更快,在更老的核心i7上甚至快了近两倍!现在我正在寻找解释。

这是我的基准测试。它使用memcpy复制100mb,然后使用memmove移动大约100mb;源和目标重叠。尝试了源和目标的各种“距离”。每个测试运行10次,打印平均时间。

https://gist.github.com/cruppstahl/78a57cdf937bca3d062c

这是在核心i5上的结果(Linux3.5.0-54-generic#81~Predicise1-Ubuntu SMP x86_64 GNU/Linux,gcc是4.6.3 (Ubuntu/Linaro4.6.3-1ubuntu5)。括号中的数字是源和目标之间的距离(间隙大小):

代码语言:javascript运行复制memcpy 0.0140074

memmove (002) 0.0106168

memmove (004) 0.01065

memmove (008) 0.0107917

memmove (016) 0.0107319

memmove (032) 0.0106724

memmove (064) 0.0106821

memmove (128) 0.0110633Memmove被实现为SSE优化的汇编代码,从后到前复制。它使用硬件预取将数据加载到缓存中,并将128个字节复制到XMM寄存器,然后将它们存储在目标位置。

(memcpy-ssse3-back.S,1650行以上)

代码语言:javascript运行复制L(gobble_ll_loop):

prefetchnta -0x1c0(%rsi)

prefetchnta -0x280(%rsi)

prefetchnta -0x1c0(%rdi)

prefetchnta -0x280(%rdi)

sub $0x80, %rdx

movdqu -0x10(%rsi), %xmm1

movdqu -0x20(%rsi), %xmm2

movdqu -0x30(%rsi), %xmm3

movdqu -0x40(%rsi), %xmm4

movdqu -0x50(%rsi), %xmm5

movdqu -0x60(%rsi), %xmm6

movdqu -0x70(%rsi), %xmm7

movdqu -0x80(%rsi), %xmm8

movdqa %xmm1, -0x10(%rdi)

movdqa %xmm2, -0x20(%rdi)

movdqa %xmm3, -0x30(%rdi)

movdqa %xmm4, -0x40(%rdi)

movdqa %xmm5, -0x50(%rdi)

movdqa %xmm6, -0x60(%rdi)

movdqa %xmm7, -0x70(%rdi)

movdqa %xmm8, -0x80(%rdi)

lea -0x80(%rsi), %rsi

lea -0x80(%rdi), %rdi

jae L(gobble_ll_loop)为什么memmove比memcpy更快?我希望memcpy复制内存分页,这应该比循环快得多。在最坏的情况下,我希望memcpy和memmove一样快。

PS:我知道我不能在我的代码中用memcpy替换memmove。我知道代码示例混合了C和C++。这个问题实际上只是出于学术目的。

更新1

我根据不同的答案运行了一些不同的测试。

当运行两次memcpy时,第二次运行比第一次快。当“接触”memcpy的目标缓冲区( faster.

memcpy )时,第一次运行的memcpy也是仍然比memmove慢一点。

结果如下:

代码语言:javascript运行复制memcpy 0.0118526

memcpy 0.0119105

memmove (002) 0.0108151

memmove (004) 0.0107122

memmove (008) 0.0107262

memmove (016) 0.0108555

memmove (032) 0.0107171

memmove (064) 0.0106437

memmove (128) 0.0106648我的结论是:根据@Oliver Charlesworth的评论,一旦memcpy目标缓冲区第一次被访问,操作系统就必须提交物理内存(如果有人知道如何“证明”这一点,请添加答案!)。此外,正如@Mats Petersson所说,memmove比memcpy缓存更友好。

感谢所有精彩的回答和评论!

相关推荐