如何将含可变比特数的NEON向量归约为单个32位值?
高效NEON SIMD比特拼接优化方案需求
需求说明
输入为两个NEON uint8x16_t SIMD寄存器:
- 第一个寄存器的每个元素低
N位为有效信息,剩余8-N位为0; - 第二个寄存器对应元素存储该
N值,且所有元素的N之和≤32。
需将这些有效比特无填充地拼接为单个32位寄存器,可调整有效比特的存储位置(如改存高N位)或元素顺序,唯一硬限制是拼接无填充且顺序固定。
现有实现方案
1. 循环累加的朴素方案
uint32_t bit_reduce_naive(uint8x16_t bits, uint8x16_t bit_counts) { uint32_t result = 0; for (size_t i = 0; i != 16; ++i) { result <<= bit_counts[i]; result |= bits[i]; } return result; }
2. 迭代合并的SIMD优化方案
先实现u8转u16的归约步骤:
inline std::pair<uint16x8_t, uint16x8_t> reduce_step_u8(uint8x16_t bits, uint8x16_t bit_counts) { const uint16x8_t expanded_count_lower = vmovl_u8(vget_low_u8(bit_counts)); const uint16x8_t expanded_bits_lower = vmovl_u8(vget_low_u8(bits)); const uint16x8_t expanded_bits_higher = vmovl_high_u8(bits); return std::pair { vorrq_u16(expanded_bits_lower, vshlq_u16(expanded_bits_higher, expanded_count_lower)), vaddl_u8( vget_low_u8(bit_counts), vget_high_u8(bit_counts)), }; }
再通过链式调用将输入归约为两个uint64_t值后合并:
uint32_t bit_reduce_better(uint8x16_t bits, uint8x16_t bit_counts) { const auto [bits_u16, counts_u16] = reduce_step_u8(bits, bit_counts); const auto [bits_u32, counts_u32] = reduce_step_u16(bits_u16, counts_u16); const auto [bits_u64, counts_u64] = reduce_step_u32(bits_u32, counts_u32); const uint64_t low_bits = vget_low_u64(bits_u64)[0]; const uint64_t high_bits = vget_low_u64(bits_u64)[0]; // 注:原代码此处疑似笔误,应为vget_high_u64 const uint64_t low_bits_count = vget_high_u64(counts_u64)[0]; return static_cast<uint32_t>(low_bits | (high_bits << low_bits_count)); }
注:该SIMD方案的比特拼接顺序与原向量不同,但顺序并非硬要求。现有方案已满足功能需求,现寻求更高效的实现方案。
内容的提问来源于stack exchange,提问作者user19232978
相关产品推荐
相关产品推荐

