SIMD or Algorithm - 0011

igame2000

使用Intrinsics方法,实现SIMD处理

使用Intrinsics,可以利用硬件的SIMD指令进行处理。MMX,SSE,SSE2(AMD已经通过交叉授权取得该技术)看起来真有那么美好么?让我们拭目以待。在处理中同样使用了定点数技术。

float test_SIMD_Filter() {
  // 模拟申请X1024 32bpp的图像内存
  __m128i* buf = (__m128i *)_mm_malloc(1024 * 1024 * sizeof(int),16);

  // 0x0000FFFF0000FFFF0000FFFF0000FFFF 用于进行计算过滤比值的65535
  __m128i _65535 = _mm_set1_epi32(0xFFFF);

  // 背景色,假设为0xFFF8F8F8
  __m128i pixel_bg = _mm_set_epi32(0x000000FF, 0x000000F8, 0x000000F8, 0x000000F8);

  // 用于滤掉颜色中的ALPHA分量
  __m128i noalpha_mask = _mm_set_epi32(0x000000FF, 0x000000FF, 0x0000000FF,0x0000000FF);

  // 小于这个颜色的值将被滤掉
  __m128i filter_val = _mm_set_epi32(0x00000000, 0x00000008, 0x000000008, 0x00000008);
  __m128i* ptr = buf; // 图像数据指针

  //先初始化数据

  for ( int h = 0; h < 1024; h++ ) { // 按行循环
    for ( int w = 0; w < 1024 / 4; w++ ) { // 一次中四个点(4X32 = 128)
      // 将4个像素设为比较有特色的数值
      *(__m128i *)ptr = _mm_set_epi32(0xFF112233, 0xFF445566, 0xFF778899, 0xFFAABBCC);
      ptr++; // 下四个点
    } // for
  } // for
  
  ptr = buf; // 开始模拟处理
  BEGIN_PERF() // 开始计数
  
  for ( int h = 0; h < 1024; h++ ) { // 还是按行循环
    for ( int w = 0; w < 1024 / 4; w++ ) { // 一次四个点
      __m128i pixel = *ptr; // 4个像素:0xFF112233 0xFF445566 0xFF778899 0xFFAABBCC

      // 取出前两个像素,成为->00FF, 0011, 0022, 0033, 00FF, 0044, 0055, 0066
      __m128i pixel_1234 = _mm_unpacklo_epi8(pixel, _mm_setzero_si128());

      //取出前后两个像素,成为->00FF, 0077, 0088, 0099, 00FF, 00AA, 00BB, 00CC
      __m128i pixel_5678 = _mm_unpackhi_epi8(pixel, _mm_setzero_si128());

      //因为涉及到32位乘法,所以还需要将像素的颜色分量扩展成32位格式
      //->00000000 00000011 00000022 00000033 第一个像素
      __m128i pixel_12 = _mm_unpacklo_epi8(pixel_1234, _mm_setzero_si128());
      
      //->00000000 00000044 00000055 00000066 第二个像素
      __m128i pixel_34 = _mm_unpackhi_epi8(pixel_1234, _mm_setzero_si128());

      //->00000000 00000077 00000088 00000099 第三个像素
      __m128i pixel_56 = _mm_unpacklo_epi8(pixel_5678, _mm_setzero_si128());

      //->00000000 000000AA 000000BB 000000CC 第四个像素
      __m128i pixel_78 = _mm_unpackhi_epi8(pixel_5678, _mm_setzero_si128());

      //先减,然后比较是否为0,类似于传统方法中的减法和&操作

      __m128i cmp_res = _mm_cmplt_epi32(_mm_sub_epi32(noalpha_mask, pixel_12), filter_val);
      __m128i delta, bg; // 过滤比值,背景色

      //先判断第一个像素
      if ( _mm_cvtsi128_si32(cmp_res) != 0 ) { // 为了比较,需要转换成整数
        // 计算过滤比值
        delta = _mm_slli_epi32(pixel_12, 8);

        // 分别计算位的低,高位,然后再或(高位要左移位),得到过滤后的背景色
        bg = _mm_or_si128( _mm_mullo_epi16(pixel_bg, delta),
        _mm_slli_epi32(_mm_mulhi_epu16(pixel_bg, delta), 16));
        delta = _mm_sub_epi32(_65535, delta); // 65535-比例为原像素的比例

        // 与计算背景过滤的方法相同
        pixel_12 = _mm_or_si128(_mm_mullo_epi16(pixel_12, delta),
        _mm_slli_epi32(_mm_mulhi_epu16(pixel_12, delta), 16));
        pixel_12 = _mm_srli_epi32(pixel_12, 16); // 从定点数还原
      } // if

      //第三个像素
      ....
      // 第四个像素略
      ......           
      //最后还要将数据拼装回去
      // 第1,2个像素
      pixel_12 = _mm_packs_epi32(pixel_12, pixel_34);
      // 第3,4个像素
      pixel_56 = _mm_packs_epi32(pixel_56, pixel_78);
      //写回
      *ptr++ = _mm_packs_epi16(pixel_12, pixel_56);
    } // for
  } // for

  END_PERF() // 停止计时
  _mm_free(buf); // 释放内存
  return GET_PERF(); // 返回结果
} // func

使用SIMD,一次过处理4个像素,貌似很快的说,但各种扩展操作抵消了性能增长,因此速度大幅落后于传统算法!
good lucky!

阅读 446

A babysitter.

6 声望
2 粉丝
0 条评论
你知道吗?

A babysitter.

6 声望
2 粉丝
宣传栏