如何将uint64_t中的位对解压缩为__m256i?
位对解压缩:uint64_t转__m256i(AVX2/AVX512实现)
需求说明
现有uint64_t类型数据,每连续2位代表一个数值,规则如下:
b00对应有符号字节0x00(数值0)b01对应有符号字节0x01(数值1)b11对应有符号字节0xFF(数值-1)b10为未使用状态,无需处理
需将该uint64_t解压缩为__m256i类型,保持原数据中位对的顺序。
已实现的反向打包代码
以下是将__m256i的字节打包为uint64_t位对的实现:
const __m256i vBits0 = _mm256_slli_epi16(target, 7); const int bits1 = _mm256_movemask_epi8(target); const int bits0 = _mm256_movemask_epi8(vBits0); const uint64_t withHigher = _pdep_u64(bits1, 0xAAAAAAAAAAAAAAAA); const uint64_t withLower = _pdep_u64(bits0, 0x5555555555555555); return withHigher | withLower;
AVX2解压缩实现
利用AVX2的位操作指令,将uint64_t的每个位对提取并映射为对应字节:
__m256i unpack_bits_to_bytes_avx2(uint64_t packed) { // 将uint64_t加载到256位向量低64位,其余位清零 __m256i v_packed = _mm256_set_epi64x(0, 0, 0, packed); // 提取每个位对的高位(bit1) const __m256i mask_high = _mm256_set1_epi64x(0xAAAAAAAAAAAAAAAA); __m256i v_high = _mm256_and_si256(v_packed, mask_high); v_high = _mm256_srli_epi64(v_high, 1); // 右移1位,将高位移到低位位置 // 提取每个位对的低位(bit0) const __m256i mask_low = _mm256_set1_epi64x(0x5555555555555555); __m256i v_low = _mm256_and_si256(v_packed, mask_low); // 合并高低位,得到每个位对的2位值 __m256i v_pair = _mm256_or_si256(v_high, v_low); // 映射位对到目标字节:b01→1,b11→0xFF,其余保持0 __m256i v_one = _mm256_cmpeq_epi8(v_pair, _mm256_set1_epi8(1)); v_one = _mm256_and_si256(v_one, _mm256_set1_epi8(1)); __m256i v_neg_one = _mm256_cmpeq_epi8(v_pair, _mm256_set1_epi8(3)); v_neg_one = _mm256_and_si256(v_neg_one, _mm256_set1_epi8(0xFF)); // 合并结果 return _mm256_or_si256(v_one, v_neg_one); }
实现说明
- 将
uint64_t加载到256位向量后,通过掩码分别提取每个位对的高低位; - 合并高低位得到2位的原始值;
- 通过比较和掩码操作,将
b01映射为0x01、b11映射为0xFF,b00保持0x00; - 最终向量的32个字节与原
uint64_t的32个位对顺序完全对应。
AVX512解压缩实现
利用AVX512的查找表(LUT)和VPSHUFB指令,实现更高效的映射:
__m256i unpack_bits_to_bytes_avx512(uint64_t packed) { // 将uint64_t加载到512位向量低64位,其余位清零 __m512i v_packed = _mm512_set_epi64(0, 0, 0, 0, 0, 0, 0, packed); // 提取每个位对的高低位并合并 const __m512i mask_high = _mm512_set1_epi64x(0xAAAAAAAAAAAAAAAA); __m512i v_high = _mm512_and_si512(v_packed, mask_high); v_high = _mm512_srli_epi64(v_high, 1); const __m512i mask_low = _mm512_set1_epi64x(0x5555555555555555); __m512i v_low = _mm512_and_si512(v_packed, mask_low); __m512i v_pair = _mm512_or_si512(v_high, v_low); // 构建查找表:索引0→0,1→1,2→0,3→0xFF(对应b00、b01、b10、b11) const __m512i lut = _mm512_setr_epi8( 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF, 0, 1, 0, 0xFF ); // 通过查找表映射得到结果 __m512i v_result = _mm512_shuffle_epi8(lut, v_pair); // 截断为__m256i返回 return _mm256_castsi512_si256(v_result); }
实现说明
- 提取位对的逻辑与AVX2一致,但使用512位向量操作;
- 预定义查找表,直接通过
VPSHUFB指令将2位的位对值映射为目标字节; - 最后截断为256位向量返回,效率比AVX2版本更高。
内容的提问来源于stack exchange,提问作者Serge Rogatch
相关产品推荐
相关产品推荐

