memcpy()
和memmove()
之间的{p> The key difference是memmove()
在源和目标重叠时可以正常工作。缓冲区肯定不会重叠memcpy() is preferable,因为它可能更快。
让我感到困扰的是潜在的。它是一个微观优化还是在memcpy()
更快的时候有真正重要的例子,以便我们真的需要使用memcpy()
而不是坚持memmove()
无处不在?
答案 0 :(得分:19)
如果编译器无法推断出无法进行重叠,则至少有一个隐式分支可以向前或向后复制memmove()
。这意味着如果不能优化memcpy()
,memmove()
至少会被一个分支放慢,并且内联指令占用的任何额外空间都可以处理每种情况(如果可以内联)。 / p>
阅读eglibc-2.11.1
和memcpy()
的memmove()
代码,确认这是可疑的。此外,在向后复制期间不可能进行页面复制,只有在没有重叠的情况下才能获得显着的加速。
总之,这意味着:如果您可以保证区域不重叠,那么选择memcpy()
超过memmove()
可以避免分支。如果源和目标包含对应的页面对齐和页面大小的区域,并且不重叠,则无论您是调用memmove()
还是memcpy()
,某些体系结构都可以为这些区域使用硬件加速副本。
除了上面列出的假设和观察之外,实际上还有一个区别。从C99开始,这两个函数存在以下原型:
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
void *memmove(void * s1, const void * s2, size_t n);
由于能够假设2个指针s1
和s2
没有指向重叠内存,memcpy
的简单C实现能够利用它来生成更高效的代码而无需求助于汇编程序,请参阅here了解更多信息。我确信memmove
可以做到这一点,但是在我eglibc
中看到的那些上面需要进行额外的检查,这意味着对于这些函数的C实现,性能成本可能略高于单个分支。
答案 1 :(得分:13)
充其量,调用memcpy
而不是memmove
将保存指针比较和条件分支。对于大型副本,这是完全无关紧要的。如果您正在做许多小型副本,那么可能值得衡量差异;这是你判断它是否重要的唯一方法。
这绝对是一种微观优化,但这并不意味着当您可以轻松证明它是安全的时候不应该使用memcpy
。过早的悲观情绪是多恶的根源。
答案 2 :(得分:4)
好吧,memmove
必须在源和目标重叠时向后复制,和源位于目标之前。因此,memmove
的某些实现只是在源位于目标之前时向后复制,而不考虑这两个区域是否重叠。
memmove
的质量实现可以检测区域是否重叠,并在不执行时进行正向复制。在这种情况下,与memcpy
相比,唯一的额外开销只是重叠检查。
答案 3 :(得分:2)
简单地说,memmove
需要测试重叠然后做适当的事情;使用memcpy
,一个断言没有重叠,因此不需要额外的测试。
话虽如此,我看到的平台与memcpy
和memmove
具有完全相同的代码。
答案 4 :(得分:2)
memcpy
肯定只是对memmove
的调用,在这种情况下使用memcpy
没有任何好处。另一方面,假设memmove
假设memcpy
很少被使用,并且在C中使用最简单的一次一个字节循环来实现它,在这种情况下它可能比一个慢十倍。优化memmove
。正如其他人所说,最有可能的情况是memcpy
在检测到正向拷贝是可能的时使用memmove
,但有些实现可能只是比较源地址和目标地址而不寻找重叠。
话虽如此,我建议不要使用memmove
,除非你在一个缓冲区内移动数据。它可能不会慢,但是可能会更快,所以当你知道不需要{{1}}时,为什么要冒风险?
答案 5 :(得分:2)
简化并始终使用memmove
。一直都是正确的功能比只有一半时间的功能更好。
答案 6 :(得分:2)
在大多数实现中,完全有可能在任何定义了两者行为的场景中,memmove()函数调用的成本都不会比memcpy()大得多。但是还有两点尚未提及:
mov esi,[src] mov edi,[dest] mov ecx,1234/4 ; Compiler could notice it's a constant cld rep movsl这将采用相同数量的内联代码,但运行速度比:
push [src] push [dest] push dword 1234 call _memcpy ... _memcpy: push ebp mov ebp,esp mov ecx,[ebp+numbytes] test ecx,3 ; See if it's a multiple of four jz multiple_of_four multiple_of_four: push esi ; Can't know if caller needs this value preserved push edi ; Can't know if caller needs this value preserved mov esi,[ebp+src] mov edi,[ebp+dest] rep movsl pop edi pop esi ret
相当多的编译器将使用memcpy()执行此类优化。虽然在某些情况下memcpy的优化版本可能提供与memmove相同的语义,但我不知道有任何与memmove有关的内容。例如,如果numbytes为20:
; Assuming values in eax, ebx, ecx, edx, esi, and edi are not needed mov esi,[src] mov eax,[esi] mov ebx,[esi+4] mov ecx,[esi+8] mov edx,[esi+12] mov edi,[esi+16] mov esi,[dest] mov [esi],eax mov [esi+4],ebx mov [esi+8],ecx mov [esi+12],edx mov [esi+16],edi
即使地址范围重叠,这也能正常工作,因为它有效地使整个区域的副本(在寄存器中)在写入任何区域之前被移动。从理论上讲,编译器可以处理memmove(),看看是否将其作为memcpy()生成即使地址范围重叠也会安全的实现,并且在替换memcpy()实现的情况下调用_memmove安全。不过,我不知道有什么可以做这样的优化。