AArch64 SIMD/ARM NEON中如何转向量比较掩码为位掩码并查询匹配?
NEON实现匹配索引与位掩码的解决方案
获取首个匹配的索引
NEON没有直接对应AVX2 tzcnt的指令,但可以通过位操作+编译器内置函数实现相同效果:
- 从比较结果(
uint8x16_t,匹配为0xFF,不匹配为0x00)中提取每个字节的最低位(匹配则为1,不匹配则为0):uint8x16_t match_bits = vandq_u8(result, vdupq_n_u8(1)); - 将16个8位的位值打包成一个16位整数:
// 合并低8位和高8位为16位掩码 uint16_t mask = vget_lane_u16(vpaddlq_u8(match_bits), 0); mask |= vget_lane_u16(vpaddlq_u8(vextq_u8(match_bits, match_bits, 8)), 0) << 8; - 用内置函数查找第一个置位位的索引,等价于
tzcnt:
注:int first_match_idx = __builtin_ctz(mask);__builtin_ctz是GCC/Clang支持的ARM架构内置函数,若需纯指令实现,可通过rbit+clz组合:int first_match_idx = __builtin_clz(__builtin_rbit(mask)) - 16;
生成匹配位掩码(类似AVX2 movemask)
要将每个字节的匹配状态转换为对应位(匹配为1,不匹配为0),可按以下步骤高效实现:
- 提取匹配位(同上述第一步):
uint8x16_t match_bits = vandq_u8(result, vdupq_n_u8(1)); - 批量移位合并为16位掩码:
以"ABAA"为例,此代码会生成// 生成移位因子:低8位对应0-7位偏移,高8位对应8-15位偏移 uint16x8_t shift_low = vcreate_u16(0x0706050403020100ULL); uint16x8_t shift_high = vaddq_u16(shift_low, vdupq_n_u16(8)); // 处理低8位,将每个位移到对应位置后求和合并 uint16x8_t vec_low = vreinterpretq_u16_u8(match_bits); uint16_t mask_low = vaddvq_u16(vshlq_u16(vec_low, shift_low)); // 处理高8位,同理合并 uint16x8_t vec_high = vreinterpretq_u16_u8(vextq_u8(match_bits, match_bits, 8)); uint16_t mask_high = vaddvq_u16(vshlq_u16(vec_high, shift_high)); // 最终得到16位掩码,第i位对应输入第i个字节的匹配状态 uint16_t full_mask = mask_low | mask_high;0b1101(即十进制13),对应第0、2、3位为1,第1位为0。
注意事项
- 上述代码假设输入是16字节的
uint8x16_t,若处理其他长度的NEON寄存器,可调整移位因子和合并逻辑。 - 确保编译器开启NEON优化(如
-mfpu=neon),以生成高效的ARM指令。
内容的提问来源于stack exchange,提问作者Stan
相关产品推荐
相关产品推荐

