基于AVX2(及AVX512)的__m256i向量符号位打包至64位整数字节的实现问询
嘿,这个需求刚好能用AVX2/AVX512的SIMD指令高效实现,我分两种指令集给你拆解具体步骤:
一、AVX2实现方案
你的__m256i向量里包含8个32位有符号整数,我们要把每个整数的符号状态转换成对应字节(非负→0x01,负数→0xFF),最后打包成一个64位整数。具体步骤如下:
生成符号位掩码:
用右移31位的方式提取每个32位整数的符号位,符号位会自动扩展:非负整数右移后得到0x00000000,负数得到0xFFFFFFFF。__m256i v = ...; // 输入的打包32位有符号整数向量 __m256i sign_mask = _mm256_srai_epi32(v, 31);将32位掩码压缩为连续字节:
我们需要把每个32位元素的低8位(0或0xFF)提取到连续的字节位置,这里用_mm256_shuffle_epi8配合自定义掩码来实现:// 洗牌掩码:选取每个32位元素的第0字节(即低8位),放到结果的前8个字节 __m256i shuffle_mask = _mm256_setr_epi8( 0,4,8,12,16,20,24,28, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1 ); __m256i sign_bytes = _mm256_shuffle_epi8(sign_mask, shuffle_mask);转换为目标字节值:
当前sign_bytes的前8字节是0(对应非负)或0xFF(对应负数),我们需要把0替换为0x01,0xFF保持不变——直接和全0x01的向量做或运算即可:__m256i target_bytes = _mm256_or_si256(sign_bytes, _mm256_set1_epi8(0x01));打包为64位整数:
提取target_bytes的低64位,就是我们要的结果:uint64_t result = _mm_cvtsi128_si64(_mm256_castsi256_si128(target_bytes));
你也可以用_mm256_cmpge_epi32来生成掩码,逻辑类似:先比较输入向量和零向量,得到非负为0xFFFFFFFF、负数为0的掩码,取反后再按上述步骤处理即可。
二、AVX512实现方案
AVX512提供了更直接的指令,能大幅简化流程:
生成符号比较掩码:
用_mm256_cmpge_epi32_mask直接得到每个32位元素是否非负的位掩码(8位,每一位对应一个元素):__m256i v = ...; __mmask8 non_neg_mask = _mm256_cmpge_epi32_mask(v, _mm256_setzero_si256());转换为目标字节序列并打包:
利用SIMD指令一次性生成目标字节向量,再提取低64位即可:__m128i byte_vec = _mm_setr_epi8( (non_neg_mask&1)?0x01:0xFF, (non_neg_mask&2)?0x01:0xFF, (non_neg_mask&4)?0x01:0xFF, (non_neg_mask&8)?0x01:0xFF, (non_neg_mask&16)?0x01:0xFF, (non_neg_mask&32)?0x01:0xFF, (non_neg_mask&64)?0x01:0xFF, (non_neg_mask&128)?0x01:0xFF, 0,0,0,0,0,0,0,0 ); uint64_t result = _mm_cvtsi128_si64(byte_vec);
如果是批量处理场景,还可以用AVX512的_mm512_maskz_mov_epi8直接生成目标字节向量,进一步提升效率。
备注:内容来源于stack exchange,提问作者Serge Rogatch

