x86平台SIMD编程入门(4):整型指令

打印 上一主题 下一主题

主题 917|帖子 917|积分 2751

1、算术指令

算术类型函数示例加_mm_add_epi32、_mm256_sub_epi16减_mm_sub_epi32、_mm256_sub_epi16乘_mm_mul_epi32、_mm_mullo_epi32除无水平加/减_mm_hadd_epi16、_mm256_hsub_epi32饱和加/减_mm_adds_epi8、_mm256_subs_epi16最大/最小值_mm_max_epu8、_mm256_min_epi32绝对值_mm_abs_epi16、_mm256_abs_epi32平均值_mm_avg_epu16、_mm256_avg_epu8没有整数除法的SIMD指令。如果要将所有通道都除以一个编译时常数,可以使用一个小技巧:编写一个函数,将相同类型的标量除以该常数,然后使用Compiler Explorer编译成汇编指令,最后移植成相应SIMD指令。例如,要把uint16_t类型的整数除以11,则上述技巧的操作过程如下:
  1. // STEP1: 写一个计算除法的普通函数
  2. #include <cstdint>
  3. uint16_t div11(uint16_t a)
  4. {
  5.     return a / 11;
  6. }
  7. // STEP2: 将上面的代码复制到Compiler Explorer中,生成对应的汇编代码如下
  8. div11(unsigned short):
  9.         push    rbp
  10.         mov     rbp, rsp
  11.         mov     eax, edi
  12.         mov     WORD PTR [rbp-4], ax
  13.         movzx   eax, WORD PTR [rbp-4]
  14.         movzx   eax, ax
  15.         imul    eax, eax, 47663
  16.         shr     eax, 16
  17.         shr     ax, 3
  18.         pop     rbp
  19.         ret
  20. // STEP3: 参考上述汇编代码中的计算方式,编写对应的SIMD指令
  21. __m128i div_by_11_epu16(__m128i x)
  22. {
  23.     x = _mm_mulhi_epu16(x, _mm_set1_epi16((short)47663));
  24.     return _mm_srli_epi16(x, 3);
  25. }
复制代码
整数指令中有一类比较“奇怪”指令,是_mm_sad_epu8(SSE2)和_mm256_sad_epu8(AVX2),它们的运算逻辑相当于以下代码:
  1. array<uint64_t, 4> avx2_sad_epu8(array<uint8_t, 32> a, array<uint8_t, 32> b)
  2. {
  3.     array<uint64_t, 4> result;
  4.     for (int i = 0; i < 4; i++)
  5.     {
  6.         uint16_t totalAbsDiff = 0;
  7.         for (int j = 0; j < 8; j++)
  8.         {
  9.             const uint8_t va = a[i * 8 + j];
  10.             const uint8_t vb = b[i * 8 + j];
  11.             const int absDiff = abs((int)va - (int)vb);
  12.             totalAbsDiff += (uint16_t)absDiff;
  13.         }
  14.         result[i] = totalAbsDiff;
  15.     }
  16.     return result;
  17. }
复制代码
它们可能最初是为了视频编码器设计的,用于估算压缩误差。不过这些指令也可以用来做与视频编码无关的事,例如用它们来计算所有字节的总和就非常快速,只要把_mm_sad_epu8第二个参数设为全零向量,然后使用_mm_add_epi64累加结果即可。
2、比较指令

运算符函数示例等于_mm_cmpeq_epi8、_mm256_cmpeq_epi64大于_mm_cmpgt_epi8、_mm256_cmpgt_epi64小于_mm_cmplt_epi8、_mm_cmplt_epi16、_mm_cmplt_epi32整数比较指令只有全通道的版本。与浮点数比较指令类似,整数比较结果也会被设置成全0或者全1。全1的有符号整数等于-1,若要统计比较结果为真的数量,一个技巧是使用下面代码所示的整数减法。使用这个技巧时要注意累加器的整数溢出问题,解决这个问题的一种方法是嵌套循环,内循环保证累加器不会溢出,外循环把内循环的累加结果投射到更宽的整数类型上。
  1. const __m128i cmp = _mm_cmpgt_epi32(val, threshold);
  2. acc = _mm_sub_epi32(acc, cmp); // acc是保存计数的累加器
复制代码
没有小于等于或大于等于的整数比较指令。如果要比较a
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

北冰洋以北

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表