跳到內容

AVX2實作strlen教學

C風格的字串結尾byte是0,如果普通的方式找0就是一個個用lodsb然後 test al, al 判斷是否為0
比起這個做法我們可以用ymm一次處理32 bytes快速找0
以下將流程拆解引導,需要解答可以展開折疊的內容

首先將內容地址載入rdi,無論從參數還是定義好的
lea rdi, s
每一次找32 bytes,先讓一個暫存器清零即可,以後找完再+32像是每次翻頁一樣
xor rax, rax
我們要比對的是有沒有0,所以用vpxor將一個ymm暫存器清零
vpxor ymm1, ymm1, ymm1
進入loop,首先用vmovdqu(如果未對齊32 bytes)載入YWORD內容到一個ymm暫存器,要記得將rdi加上第二步清零的暫存器
vmovdqu ymm0, YWORD [rdi+rax]
使用vpcmpeqb比對兩個暫存器,字串和全00的,如果比對到00和00就會得到FF
vpcmpeqb ymm0, ymm0, ymm1
使用vpmovmskb做成32 bits的mask
vpmovmskb edx, ymm0
判斷是否為0,如果有過FF遮罩會出現1,整個數字就不會是0
test edx, edx
非零的話可以跳到exit了
jnz exit
在exit有一個tzcnt reg32,rm32指令取得右邊0的個數,比如10000會得到5
tzcnt edx, edx
我們應該將tzcnt的結果加上每次翻頁+32的暫存器,exit的區塊就完成了
add rax, rdx
回到判斷非零跳轉的下一行,假設沒跳轉只要加上32再回到loop即可
; 上一行 jnz exit
add rax, 32
jmp loop
完整範例代碼
global strlen
section .text
strlen:
lea rdi, s
xor rax, rax
vpxor ymm1, ymm1, ymm1
loop:
vmovdqu ymm0, YWORD [rdi+rax]
vpcmpeqb ymm0, ymm0, ymm1
vpmovmskb edx, ymm0
test edx, edx
jnz exit
add rax, 32
jmp loop
exit:
tzcnt edx, edx
add rax, rdx
ret

在glibc的strlen-avx2.S也可以看到差不多的實現方式