如何在SSE2中实现8位和16位整数的向量左右位移?
仅用SSE2实现8位/16位整数向量可变左移方案
核心实现思路
SSE2无直接的可变位位移指令,但可通过拆包元素+按位移量移位+合并结果的组合逻辑模拟,核心是利用SSE2支持的固定位宽移位指令,配合拆包/合并操作适配8位、16位元素的可变移位需求。
8位整数可变左移(模拟_mm_sllv_epi8)
__m128i mm_sllv_epi8_sse2(__m128i src, __m128i shifts) { // 限制位移量在0-7(8位元素最大有效移位) shifts = _mm_and_si128(shifts, _mm_set1_epi8(0x7)); // 将8位元素拆包为16位,避免移位溢出 __m128i src_lo = _mm_unpacklo_epi8(src, _mm_setzero_si128()); __m128i src_hi = _mm_unpackhi_epi8(src, _mm_setzero_si128()); __m128i shift_lo = _mm_unpacklo_epi8(shifts, _mm_setzero_si128()); __m128i shift_hi = _mm_unpackhi_epi8(shifts, _mm_setzero_si128()); // 对16位元素执行固定位左移(SSE2原生支持) src_lo = _mm_sll_epi16(src_lo, shift_lo); src_hi = _mm_sll_epi16(src_hi, shift_hi); // 合并回8位向量,自动截断高位溢出部分 return _mm_packus_epi16(src_lo, src_hi); }
注:
_mm_packus_epi16会保留移位后元素的低8位,正好匹配8位左移的结果要求;位移量提前限制在0-7,避免无效移位。
16位整数可变左移(模拟_mm_sllv_epi16)
__m128i mm_sllv_epi16_sse2(__m128i src, __m128i shifts) { // 限制位移量在0-15(16位元素最大有效移位) shifts = _mm_and_si128(shifts, _mm_set1_epi16(0xF)); // 将16位元素拆包为32位 __m128i src_lo = _mm_unpacklo_epi16(src, _mm_setzero_si128()); __m128i src_hi = _mm_unpackhi_epi16(src, _mm_setzero_si128()); __m128i shift_lo = _mm_unpacklo_epi16(shifts, _mm_setzero_si128()); __m128i shift_hi = _mm_unpackhi_epi16(shifts, _mm_setzero_si128()); // 对32位元素执行固定位左移 src_lo = _mm_sll_epi32(src_lo, shift_lo); src_hi = _mm_sll_epi32(src_hi, shift_hi); // 合并回16位向量,截断高位溢出部分 return _mm_packus_epi32(src_lo, src_hi); }
注:逻辑与8位版本一致,通过32位移位适配16位元素的可变位移需求,最终合并时保留低16位结果。
性能说明
该方案依赖拆包、移位、合并三次核心操作,性能弱于AVX2原生指令,但在仅支持SSE2的平台上是可行的替代方案。若需优化,可预先缓存常用位移量的处理掩码,减少运行时计算开销。
内容的提问来源于stack exchange,提问作者dave_thenerd
相关产品推荐
相关产品推荐

