x86 指令集提供了專用的字串操作指令(LODSB、STOSB、MOVSB、CMPSB、SCASB 等),配合 REP/REPE/REPNE 前綴可實現隱含迴圈。然而,在現代處理器 (Pentium Pro 以降的 Out-of-Order 微架構) 上,這些指令往往比手寫的暫存器間接定址迴圈更慢。本文分析原因並提供最佳化範例。
REP MOVSB 內部涉及複雜微碼流程,包含計數器檢查、旗標更新、例外處理等,難以有效流水線化。ESI/EDI/ECX/EFLAGS,限制編譯器/程式設計師的排程自由度。MOVDQU/VMOVDQU 等)。REP 迴圈的結束條件在微碼層級處理,難以利用巨集融合與分支預測器優化。; LODSB 等價
MOV AL, BYTE PTR [ESI]
INC ESI
; STOSB 等價
MOV BYTE PTR [EDI], AL
INC EDI
; MOVSB 等價
MOV AL, BYTE PTR [ESI]
MOV BYTE PTR [EDI], AL
INC ESI
INC EDI
; ECX = 位元組數
MOV EBX, ECX
SHR ECX, 2 ; ECX = 雙字數
REP MOVSD ; 複製 4 位元組塊
MOV ECX, EBX
AND ECX, 3 ; 剩餘 0~3 位元組
REP MOVSB
; 假設 ESI、EDI 16-byte 對齊,ECX 為 16 的倍數
MOV EBX, ECX
SHR ECX, 4 ; 16-byte 區塊數
.copy_loop:
MOVDQU XMM0, [ESI]
MOVDQU [EDI], XMM0
ADD ESI, 16
ADD EDI, 16
DEC ECX
JNZ .copy_loop
; 處理剩餘位元組...
lstrcpyn proc uses esi edi ecx, src:DWORD, des:DWORD, ilen:DWORD
mov ecx, ilen
mov esi, src
mov edi, des
@@:
mov al, byte ptr [esi]
inc esi
mov byte ptr [edi], al
cmp al, 0 ; 遇到結束字元提前結束
jz @F
loopnz @B ; ECX 遞減至 0 或 ZF=1 結束
@@:
ret
lstrcpyn endp
此實作:
USES 自動保存/恢復 ESI/EDI/ECX。NULL)。LOOPNZ 結合計數器與零旗標,單指令實現雙重結束條件。REP MOVSB 的微碼開銷,在短字串 (典型 < 256 字節) 上顯著更快。| 實作方式 | 週期數 (估) | 備註 |
|---|---|---|
REP MOVSB | ~120 | 微碼啟動開銷大 |
手寫 MOVSB 迴圈 | ~45 | 單位指令、易預測 |
MOVSD + MOVSB 餘數 | ~28 | 減少 75% 迴圈次數 |
SSE2 MOVDQU 16-byte | ~12 | 需對齊、CPU 支援檢查 |