跳转到内容

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也可以看到差不多的实现方式