你有正确的想法_mm256_cmpeq_epi8- > _mm256_movemask_epi8.AFAIK,这是至少为Intel CPU实现此功能的最佳方式. PMOVMSKB r32, ymm与XMM 16字节版本的速度相同,因此解压缩256b向量的两个通道并分别移动它们然后重新组合整数结果将是一个巨大的损失.(来源:Agner Fog的指令表.请参阅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的asmlib.)
许多glibc版本扫描到对齐边界,然后使用一次读取64B的展开循环(在4个SSE向量中,因为我不认为glibc有AVX2版本).
要优化长字符串,通过将比较结果进行OR运算来减少测试比较结果的开销,并检查它.如果你找到一个命中,请返回并重新测试你的向量,看看哪个向量有命中.
使用ffs多个movemask结果(使用shift和|)构建一个64位整数可能会更有效一些.我不确定在测试零之前在循环内执行此操作; 我不记得glibc的一个strlen策略是否做到了.
我在这里建议的所有内容都可以在asm中以strlen,memchr和相关函数的各种glibc策略看到.这是sysdeps/x86_64/strlen.S,但是我可能在某个地方使用了超过基线SSE2的其他源文件.(或者不是,我可能会想到一个不同的功能,也许除了AVX(3操作数insn)和AVX2(256b整数向量)之外没有任何东西可以获得超越SSE2.
也可以看看:
strchr-avx2.S (Woboq.org有一个很好的源浏览器,有用的搜索文件名/符号).memchr-avx2.Sglibc的memchr使用PMAXUB而不是POR.我不确定这是否对某些神秘的微体系结构有用,但它在大多数CPU上运行的端口较少.也许这是期望的,以避免资源与其他东西发生冲突?IDK似乎很奇怪,因为它与PCMPEQB竞争.