如何用Intel intrinsics拆分__m128i 16位向量为奇偶位两个向量?
使用Intel Intrinsics拆分16位向量为32位实部/虚部向量
可以通过Intel Intrinsics实现该操作,以下是基于SSE指令集的具体实现方案:
一、拆分已有的__m128i向量(8×16bit)
假设已有初始化完成的__m128i向量,包含8个16位元素,以下是兼容SSE2的实现(适配多数现代x86 CPU):
#include <emmintrin.h> // 引入SSE2头文件 // 初始化8个16位元素的向量 __m128i a = _mm_setr_epi16(1, 2, 3, 4, 5, 6, 7, 8); __m128i zero = _mm_setzero_si128(); // 掩码:提取奇数位置的16位元素(1,3,5,7),高4个16位位置清零 __m128i mask_x = _mm_setr_epi8(0,1,4,5,8,9,12,13, -1,-1,-1,-1,-1,-1,-1,-1); // 掩码:提取偶数位置的16位元素(2,4,6,8),高4个16位位置清零 __m128i mask_y = _mm_setr_epi8(2,3,6,7,10,11,14,15, -1,-1,-1,-1,-1,-1,-1,-1); // 提取目标16位元素 __m128i x_16 = _mm_shuffle_epi8(a, mask_x); __m128i y_16 = _mm_shuffle_epi8(a, mask_y); // 将16位元素零扩展为32位,得到最终的4个32位元素向量 __m128i x = _mm_unpacklo_epi16(x_16, zero); // x = {1, 3, 5, 7}(32位元素) __m128i y = _mm_unpacklo_epi16(y_16, zero); // y = {2, 4, 6, 8}(32位元素)
若CPU支持SSE4.1,可使用_mm_cvtepu16_epi32简化扩展步骤:
#include <smmintrin.h> // 引入SSE4.1头文件 __m128i x = _mm_cvtepu16_epi32(x_16); __m128i y = _mm_cvtepu16_epi32(y_16);
二、直接从内存拆分16位数据流
如果原始16位数据存储在内存中,可直接加载并拆分,无需额外中间步骤:
#include <emmintrin.h> uint16_t data[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 从内存加载128位数据到__m128i向量 __m128i a = _mm_load_si128((__m128i*)data); // 后续拆分步骤与上述一致 __m128i zero = _mm_setzero_si128(); __m128i mask_x = _mm_setr_epi8(0,1,4,5,8,9,12,13, -1,-1,-1,-1,-1,-1,-1,-1); __m128i mask_y = _mm_setr_epi8(2,3,6,7,10,11,14,15, -1,-1,-1,-1,-1,-1,-1,-1); __m128i x_16 = _mm_shuffle_epi8(a, mask_x); __m128i y_16 = _mm_shuffle_epi8(a, mask_y); __m128i x = _mm_unpacklo_epi16(x_16, zero); __m128i y = _mm_unpacklo_epi16(y_16, zero);
关键指令说明
_mm_shuffle_epi8:按字节粒度重新排列输入向量元素,掩码中-1(0xFF)表示对应位置清零。_mm_unpacklo_epi16:将两个输入向量的低4个16位元素交替组合,与零向量配合实现16位到32位的零扩展。- 若处理有符号16位数据,需符号扩展时,可将零向量替换为全1向量(
_mm_set1_epi16(-1)),或使用SSE4.1的_mm_cvtepi16_epi32。
内容的提问来源于stack exchange,提问作者Zvi Vered
相关产品推荐
相关产品推荐

