我正在尝试找到角色的第一个实例,在这种情况下'"''使用simd(AVX2或更早版本)。我想使用_mm256_cmpeq_epi8,但是我需要一种快速的方法来查找__m256i中的任何结果字节是否已设置为0xFF。然后计划使用_mm256_movemask_epi8将结果从字节转换为位,并使用ffs获取匹配的索引。使用_mm_movemask_epi8一次移出一部分会更好吗?还有其他建议吗?
答案 0 :(得分:5)
_mm256_cmpeq_epi8
- >您有正确的想法_mm256_movemask_epi8
。 AFAIK,这是至少为Intel CPU实现此功能的最佳方式。 PMOVMSKB r32, ymm
与XMM 16字节版本的速度相同,因此解压256b向量的两个通道并分别移动它们然后重新组合整数结果将是一个巨大的损失。 (来源:Agner Fog's instruction table。请参阅x86代码wiki中的其他perf链接。)
在ffs
确定非零结果后,离开_mm256_movemask_epi8
,使循环内的代码尽可能高效。
TEST / JCC可以宏融合为单个uop,但BSF / JCC不能,因此需要额外的指令。 (并且你很难让C编译器发出BSF / JCC。更可能分支ffs
的结果会给你一些输入为非零的测试,那么BSF,然后加1,然后比较和分支。与仅测试movemask结果相比,这显然是可怕的。)
另请注意,对于类似的问题,比较movemask(例如检查它的0xFFFFFFFF)和分支是非零的一样有效。
正如Paul R所建议的,看一些strlen,strchr和memchr实现可能会提供信息。在开源libc实现和其他地方有多个手写的asm实现。 (例如glibc和Agner Fog's asmlib。)
许多glibc的版本扫描到对齐边界,然后使用一次读取64B的展开循环(在4个SSE向量中,因为我不认为glibc有AVX2版本)。
要优化长字符串,通过将比较结果进行OR运算来减少测试比较结果的开销,并检查它。如果你找到命中,请返回并重新测试你的向量,看看哪个向量有命中。
在由多个movemask结果(使用shift和ffs
)构建的一个64位整数上执行|
可能更有效。在测试零之前,我不确定在循环内执行此操作;我不记得glibc的一个strlen策略是否做到了。
我在这里建议的所有内容都可以在asm中以strlen,memchr和相关函数的各种glibc策略中看到。这里是sysdeps/x86_64/strlen.S,但是我可能在某个地方使用了超过基线SSE2的其他源文件。 (或者不是,我可能会想到一个不同的功能,也许在SSE2之外没有任何东西可以获得,直到AVX(3操作数insn)和AVX2(256b整数向量)。
另见:
strchr-avx2.S
(Woboq.org有一个很好的源浏览器,可以搜索文件名/符号。)memchr-avx2.S
glibc's memchr使用PMAXUB代替POR。我不确定这对于某些神秘的微架构原因是否有用,但它在大多数CPU上运行的端口较少。也许这是他们想要的,以避免资源与其他东西发生冲突? IDK似乎很奇怪,因为它与PCMPEQB竞争。