[英]What are real significant cases when memcpy() is faster than memmove()?
memcpy()
和memmove()
之間的關鍵區別在於,當源和目標重疊時, memmove()
將正常工作。 當緩沖區肯定不重疊時, memcpy()更可取,因為它可能更快。
困擾我的是這個潛在的 。 它是一個微優化還是當memcpy()
更快時有真正重要的例子,所以我們真的需要使用memcpy()
而不是到處都有memmove()
?
如果編譯器無法推斷出無法重疊,那么至少有一個隱式分支可以向前或向后復制memmove()
。 這意味着如果不能優化memcpy()
, memmove()
至少會被一個分支放慢,並且內聯指令占用的任何額外空間都可以處理每種情況(如果可以內聯)。
讀取memcpy()
和memmove()
的eglibc-2.11.1
代碼可以確認這一點。 此外,在向后復制期間不可能進行頁面復制,只有在沒有重疊的情況下才能獲得顯着的加速。
總之,這意味着:如果可以保證區域不重疊,那么在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實現能夠利用它來生成更高效的代碼,而無需借助匯編程序,請參閱此處了解更多信息。 我確信memmove
可以做到這一點,但是我在eglibc
看到的那些上面需要額外的檢查,這意味着對於這些函數的C實現,性能成本可能略高於單個分支。
充其量,調用memcpy
而不是memmove
將保存指針比較和條件分支。 對於大型副本,這是完全無關緊要的。 如果您正在做許多小型副本,那么可能值得衡量差異; 這是唯一可以判斷它是否重要的方法。
它絕對是一種微觀優化,但這並不意味着當您可以輕松證明它是安全的時候不應該使用memcpy
。 過早的悲觀情緒是多惡的根源。
好吧, memmove
必須在源和目標重疊時向后復制, 並且源位於目標之前。 因此, memmove
某些實現只是在源位於目標之前時向后復制,而不考慮這兩個區域是否重疊。
memmove
的高質量實現可以檢測區域是否重疊,並在不執行時進行前向復制。 在這種情況下,與memcpy
相比,唯一的額外開銷就是重疊檢查。
簡單地說, memmove
需要測試重疊然后做適當的事情; 使用memcpy
,一個斷言沒有重疊,因此不需要額外的測試。
話雖如此,我已經看到了具有完全相同的memcpy
和memmove
代碼的平台。
memcpy
當然可能僅僅是對memmove
的調用,在這種情況下使用memcpy
沒有任何好處。 另一方面,實現者可能很少使用memmove
,並且在C中使用最簡單的一次一個字節循環來實現它,在這種情況下,它可能比優化的memcpy
慢十倍。 正如其他人所說,最有可能的情況是memmove
在檢測到正向拷貝可能時使用memcpy
,但是某些實現可能只是比較源地址和目標地址而不尋找重疊。
話雖如此,我建議永遠不要使用memmove
除非你在一個緩沖區內移動數據。 它可能不會慢,但話又說回來,那么為什么當你知道不需要memmove
時冒險呢?
只需簡化並始終使用memmove
。 一直都是正確的功能比只有一半時間的功能更好。
完全有可能在大多數實現中,memmove()函數調用的成本在定義兩者行為的任何場景中都不會比memcpy()大得多。 但是,有兩點尚未提及:
\n mov esi,[src]\n mov edi,[dest]\n mov ecx,1234/4; 編譯器可能會注意到它是一個常數\n CLD\n rep movsl\n這將采用相同數量的內聯代碼,但運行速度比:
\n 推[src]\n 推[dest]\n 推dword 1234\n 打電話給_memcpy\n\n ...\n\n _memcpy:\n 推ebp\n mov ebp,尤其是\n mov ecx,[ebp + numbytes]\n 測試ecx,3; 看看它是否是四的倍數\n jz multiple_of_four\n\n multiple_of_four:\n 推esi; 無法知道調用者是否需要保留此值\n 推edi; 無法知道調用者是否需要保留此值\n mov esi,[ebp + src]\n mov edi,[ebp + dest]\n rep movsl\n pop edi\n 流行esi\n RET \n
相當多的編譯器將使用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安全。 不過,我不知道有任何優化。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.