[英]Performance penalty with executing x86 instructions stored in the data segment?
[英]Performance of x86 rep instructions on modern (pipelined/superscalar) processors
我最近一直在編寫 x86 程序集(為了好玩),並且想知道 rep 前綴字符串指令是否在現代處理器上確實具有性能優勢,或者它們是否只是為了向后兼容而實現的。
我可以理解為什么英特爾最初會在處理器一次只運行一條指令時實現 rep 指令,但現在使用它們有好處嗎?
對於編譯為更多指令的循環,有更多指令需要填充管道和/或被亂序發出。 現代處理器是為優化這些帶有 rep 前綴的指令而構建的,還是在現代代碼中很少使用 rep 指令以至於它們對制造商來說並不重要?
AMD 和英特爾的優化指南中都為此類問題提供了大量空間。 在這方面給出的建議的有效性有一個“半衰期”——不同的 CPU 代表現不同,例如:
英特爾架構優化手冊在表 7-2 中提供了各種塊復制技術(包括rep stosd
)的性能比較數據。 內存復制例程的相對性能,pg。 7-37f.,對於不同的 CPU,再一次,在一個上最快的可能不是在其他 CPU 上最快的。
在許多情況下,最近的 x86 CPU(具有“字符串”SSE4.2 操作)可以通過 SIMD 單元執行字符串操作,請參閱此調查。
要跟進所有這些(和/或在不可避免的情況再次發生變化時讓自己保持最新狀態),請閱讀Agner Fog 的優化指南/博客。
除了 FrankH 的出色回答; 我想指出哪種方法最好還取決於字符串的長度、對齊方式以及長度是固定的還是可變的。
對於小字符串(可能最多約 16 個字節),使用簡單的指令手動執行它可能會更快,因為它避免了更復雜技術的設置成本(並且對於固定大小的字符串可以輕松展開)。 對於中等大小的字符串(可能從 16 字節到 4 KiB),諸如“REP MOVSD”之類的東西(如果可能出現錯位,則會拋出一些“MOVSB”指令)可能是最好的。
對於比這更大的任何事情,有些人會傾向於進入 SSE/AVX 和預取等。更好的主意是修復調用者/s,以便在第一個不需要復制(或 strlen() 或其他)地方。 如果你足夠努力,你幾乎總能找到方法。 注意:還要非常警惕“假定的”快速 mempcy() 例程 - 通常它們已經在大量字符串上進行了測試,而沒有在更可能的小/小/中字符串上進行測試。
另請注意,由於所有這些差異(可能的長度、對齊方式、固定或可變大小、CPU 類型等)(出於優化而非方便的目的),為所有非常不同的情況是近視的。
由於沒有人給你任何數字,我會給你一些我通過對我的垃圾收集器進行基准測試而發現的,這是非常重的 memcpy。 我要復制的對象長度為 60% 16 字節,其余 30% 為 500 - 8000 字節左右。
dst
、 src
和n
都是 8 的倍數。 這是我的三個memcpy
變體:
手工編碼的while循環:
if (n == 16) {
*dst++ = *src++;
*dst++ = *src++;
} else {
size_t n_ptrs = n / sizeof(ptr);
ptr *end = dst + n_ptrs;
while (dst < end) {
*dst++ = *src++;
}
}
( ptr
是uintptr_t
的別名)。 時間:101.16%
rep movsb
if (n == 16) {
*dst++ = *src++;
*dst++ = *src++;
} else {
asm volatile("cld\n\t"
"rep ; movsb"
: "=D" (dst), "=S" (src)
: "c" (n), "D" (dst), "S" (src)
: "memory");
}
時間:103.22%
rep movsq
if (n == 16) {
*dst++ = *src++;
*dst++ = *src++;
} else {
size_t n_ptrs = n / sizeof(ptr);
asm volatile("cld\n\t"
"rep ; movsq"
: "=D" (dst), "=S" (src)
: "c" (n_ptrs), "D" (dst), "S" (src)
: "memory");
}
時間:100.00%
req movsq
以微弱優勢獲勝。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.