4 x86 assembly sse strlen avx2
是否有任何方法可以通过将其加载到XMM或YMM寄存器中来获取存储在16字节或32字节缓冲区中的ASCII字符串的长度?本质上,我正在寻找第一个零字节的索引(以位或字节为单位)。
我的目标是避免循环和分支。我希望在AVX或SSE中,沿着BSF(向前扫描位)的方式存在某种东西,但是对字节而非位进行操作。
也许像下面这样?
_my_constant_time_strlen:
vpxor ymm0, ymm0
VPCMPEQB ymm0, ymm0, [rdi]
vpmovmskb eax, ymm0
bsf eax, eax
; string length is in eax?
; and rax, 31 ; editor's note: useless AND
ret
Run Code Online (Sandbox Code Playgroud)
这是究竟如何实现strlen或memchr与AVX2。 (对于固定大小的缓冲区,您知道缓冲区中的某处将有一个匹配项。)
(现在除外and)。
vpmovmskb将您的比较向量转换为字节比较结果的位图,您可以使用BSF搜索该位图。您的代码已经完全可以满足您的要求。
vpxor如果避免破坏它,可以将-zeroing退出循环。
真正的工作在Intel CPU(Haswell / Skylake)上总共只有3个uops:vpcmpeqb是1个微融合的uop,因为您避免了索引寻址模式。 vpmovmskb是1 uop,具有2到3个周期的延迟。 tzcnt在Intel或AMD CPU上为1 uop。(bsf在Intel上也是1 uop)。在Intel tzcnt或bsf具有3个周期的延迟。
因此,在主流Intel上,从准备好矢量加载数据到RAX长度的总延迟为1(vpcmpeqb)+ 2或3(movmsk)+ 3(tzcnt)= 6或7个周期。这是无分支的,只是数据依赖性,所以这是很合理的。无论地址或数据位于关键路径上,这都不会计算负载使用延迟或存储转发延迟。 而且吞吐量非常出色,在Intel上每个时钟只有1个脉冲(瓶颈在端口0和/或端口1上)。
在AMD Ryzen上vpcmpeqb ymm为2微秒,延迟为2c。 vpmovmskb ymm是2微秒(对于Port2),延迟为3c。 tzcnt是2微秒,延迟为2c。因此,总延迟= 7个周期,吞吐量是每2个周期1个瓶颈,这是movmsk吞吐量的瓶颈。(Ryzen lzcnt为1 uop / 1c延迟;大概tzcnt是位反转+之类的lzcnt东西。)
来自https://agner.org/optimize/的数字。
没有一条指令会扫描XMM或YMM寄存器中的第一个非零字节,唯一有效的方法是pcmpeqb/pmovmskb。vmovd xmm0, eax如果要在向量reg中使用字节位置而不是整数,则可以在最后。
但是通常您希望将结果保存在整数寄存器中。
在不先将movmsk转换为整数的情况下进行水平矢量搜索/扫描时,唯一想到的就是phminposuw,这不是您想要的。
或者也许vpand使用1,2,4,8,16, ...2 的幂的向量,然后vpsadbw对字节进行水平相加。结果中的最低设置位指示该8字节块中前0的位置。
或者,您可以执行log2(vector_length)步骤,对下一个元素进行混排和掩盖,因此最终得到一个矢量,其中输入中只有前0个0xff元素。然后,VPAND通过一个0,1,2,3,4,...与vpsadbwhsum 的向量,唯一的非零元素将是字节位置。但是,这是昂贵得多vpcmpeqb/ vpmovmskb/ bsf/ vmovd回XMM寄存器,如果你真的想要的结果由于某些原因XMM寄存器。
如果不确定是否有零字节,请使用tzcnt代替bsf,因为它会32为全零输入产生(操作数大小)。 bsf在AMD上速度也较慢,因此实际上您应该始终tzcnt在此处使用。
bsf如果输入为零,则使目标保持不变。(由AMD记录;英特尔说“未定义”,但仍然可以实现。)
和/或在位扫描后检查ZF以检测全零输入情况。(tzcnt将CF设置为全零输入,并根据输出是否为零设置ZF。)
另外,使用vpxor XMM而不是将寄存器清零YMM。在AMD上保存uop。
| 归档时间: |
|
| 查看次数: |
92 次 |
| 最近记录: |