String 测试xmm寄存器中的任何字节是否为0
我目前正在自学SIMD,并正在编写一个相当简单的字符串处理子程序。然而,我被限制在SSE2上,这使得我无法使用ptest来查找空终端 我目前试图找到空终端的方式使我的SIMD循环有>16条指令,这与使用SIMD的目的背道而驰,或者至少使它不值得String 测试xmm寄存器中的任何字节是否为0,string,assembly,x86,simd,sse2,String,Assembly,X86,Simd,Sse2,我目前正在自学SIMD,并正在编写一个相当简单的字符串处理子程序。然而,我被限制在SSE2上,这使得我无法使用ptest来查找空终端 我目前试图找到空终端的方式使我的SIMD循环有>16条指令,这与使用SIMD的目的背道而驰,或者至少使它不值得 //Check for null byte pxor xmm4, xmm4 pcmpeqb xmm4, [rdi] //Generate bitmask movq rax, xmm4 t
//Check for null byte
pxor xmm4, xmm4
pcmpeqb xmm4, [rdi] //Generate bitmask
movq rax, xmm4
test rax, 0xffffffffffffffff //Test low qword
jnz .Lepilogue
movhlps xmm4, xmm4 //Move high into low qword
movq rax, xmm4
test rax, 0xffffffffffffffff //Test high qword
jz .LsimdLoop //No terminal was found, keep looping
我在想,如果没有ptest,是否有更快的方法可以做到这一点,或者这是否是最好的方法,我将不得不进一步优化循环的其余部分
注意:我确保输入使用SIMD的循环的字符串地址是16B对齐的,以允许对齐的指令。您可以使用(pmovskb
指令)从比较结果中获得位掩码(生成的掩码包含向量中每个字节的最高有效位)。然后,测试任何字节是否为零意味着测试掩码中的16位中是否有非零位
pxor xmm4, xmm4
pcmpeqb xmm4, [rdi]
pmovmskb eax, xmm4
test eax, eax ; ZF=0 if there are any set bits = any matches
jnz .found_a_zero
找到具有任何匹配项的向量后,您可以使用bsf eax,eax
找到第一个匹配位置,以获得位掩码中的位索引,这也是16字节向量中的字节索引
或者,您可以检查与
pcmpeqb
/pmovmskb
/cmp eax、0xffff
匹配的所有字节(例如,像在memcmp/strcmp中那样),以检查是否设置了所有位,而不是检查至少1位集。@Liqs:pmovmskb
在所有x86 CPU上都很便宜。只有1个uop(英特尔上的端口0)和大约3个周期的延迟。看到或看到。如果这个基准测试比您的movq/movq/or方法慢,那么您可能会遇到其他一些性能问题。也许@Liqs:它执行的指令比你的答案,甚至是你问题中的代码都多,这是不合理的。您确定每次都在测量相同的输入数据,并且只计算进程的perf stat-e任务时钟、周期:u、指令:u
用户空间指令的事件吗?@Liqs:也相关:re:通过使用por
,在整个向量缓存线上摊销任何零测试,然后找出零的位置。如果您想要一个精确的字节位置,您可以bsf
注意到的pmovmsk
结果:。也。真正的库在strleny中使用pmovmskb
/cmp
。您的代码会找到第一个非零字节。但是OP想找到第一个零字节(只需将cmp
替换为test
)@Liqs:检查对这个答案的更新:它之前正在寻找一个不全为零的向量。如果您在没有检查正确性的情况下进行了基准测试,这可以解释为什么您的基准测试结果发现执行了更多的指令,如果您的代码实际上做了更多的工作。(测试优化的正确性和速度始终是一个好主意,尤其是当您发现执行了更多指令等令人惊讶的结果时。)参见test rax,0xffffffffffffff
相当于test rax,rax