__builtin_ia32_cvtb2mask512()
是 GNU C 内置,用于 vpmovb2m k, zmm
。_mm512_movepi8_mask
。
它从每个字节中提取最高有效位,生成一个整数掩码。
SSE2 和 AVX2 指令
pmovmskb
和 vpmovmskb
对 16 或 32 字节向量执行相同的操作,在 GPR 而不是 AVX-512 掩码寄存器中生成掩码。 (_mm_movemask_epi8
和 _mm256_movemask_epi8
)。
我已经附上了 C 语言的基本标量实现。对于那些尝试在 ARM 中实现此功能的人,我们关心高位,但每个字节的高位(在 128 位向量中)可以使用 ARM 轻松移位到低位NEON 内在函数:vshrq_n_u8()。 请注意,我不想将位图存储到内存中,它应该只是类似于以下函数的函数的返回值。
#define _(n) __attribute((vector_size(1<<n),aligned(1)))
typedef char V _(6); // 64 bytes, 512 bits
typedef unsigned long U;
#undef _
U generic_cvtb2mask512(V v) {
U mask=0;int i=0;
while(i<64){
// shift mask by 1 and OR with MSB of v[i] byte
mask=(mask<<1)|((v[i]&0x80)>>7);
i++;}
return mask;
}
这是 16 字节(128b 向量)的一种可能算法,只需将其放入 64 字节(512b 向量)的循环中:
#define _(n) __attribute((vector_size(1<<n),aligned(1)))
typedef char g4 _(4); // 16 bytes, 128 bits
typedef char g3 _(3); // 8 bytes, 64 bits
typedef unsigned long U;
#undef _
unsigned short get_16msb(g4 v) {
unsigned short = ret;
// per byte, make every bit same as msb
g4 msb = vdupq_n_u8(0x80);
g4 filled = vceqq_u8(v, msb);
// create a mask of each bit value
g4 b = {0x80, 0x40, 0x20, 0x01, 0x08, 0x04, 0x02, 0x01,
0x80, 0x40, 0x20, 0x01, 0x08, 0x04, 0x02, 0x01};
// and vectors together
g4 z = vandq_u8 (filled,b);
// extract lower 8 bytes, hi 8 bytes
g3 lo = vget_low_u8(z);
g3 hi = vget_high_u8(z);
// and the 8 bytes of lo together ...
// put in byte 1 of ret
// and the 8 bytes of hi together ...
// put in byte 2 of ret
return ret;
}
当大多数/最好的优化都是针对特定的时,想要优化通用的就会很困难。尤其是您想对结果做什么。
例如“检查是否设置了任何高位”的代码可能比“检查设置了哪个高位”便宜得多。
// per byte, make every bit same as msb
g4 msb = vdupq_n_u8(0x80);
g4 filled = vceqq_u8(v, msb);
不会对性能产生影响,但它会检查符号位是否已设置,所以只需执行
vcltzq_s8(v)
即可。即,代替 v == 0x80
,只需检查有符号比较中的值是否为负。
如果您只关心是否存在设置有符号位的值,对于 Adv SIMD,您可以在比较结果上使用
vpmaxq_s8
并执行以下操作:
if (vgetq_lane_s64 (vreinterpretq_s64_s8 (res), 0))
对于 SVE,您不需要这个,因为比较本身会设置标志。您可以对比较的谓词结果执行
ptest
并对其进行分支。编译器应该能够在优化期间删除 ptest
。
如果您需要使用哪个元素,有多种方法。 正如 Peter Cordes 在评论中所说,您可以使用带有特殊掩码的 AND 和用于 Adv 的
clz
。 SIMD。
这些模式很常见,本质上是来自标准库的
strchr
。 因此,为了获得最佳序列,我建议检查 Arm 优化例程中的所有内容,当我们找到更好的方法时,我们会不断更新这些例程。
对于 Neon:https://github.com/ARM-software/optimized-routines/blob/master/string/aarch64/strchr.S 是文件,其操作如上所述。
对于SVE:https://github.com/ARM-software/optimized-routines/blob/master/string/aarch64/strchr-sve.S那里有一些额外的代码,因为
strchr
需要检查null终结者,但总体思路是一样的。