SIMD or Algorithm - 0011

模式薄雾
• 阅读 1954

使用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!

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这