高级语言内的单指令多数据流计算(SIMD)

来源:互联网 发布:音乐分轨软件 编辑:程序博客网 时间:2024/06/07 05:17

摘要:
   很多年来,x86体系的CPU增加的新指令集大多都是SIMD指令(和相应的寄存器);
然而很容易忽视的是,我们在高级语言内也能进行很多SIMD类计算!

正文: 
    单指令多数据流,Single Instruction Multiple Data,简写为SIMD,就是说用
一个指令同一时间处理多个数据;  
    很多年来,x86体系的CPU增加的新指令集大多都是SIMD指令(和相应的寄存器);
比如MMX,3DNow!,MMX2,SSE,SSE2,SSE3,SSSE3,SSE4,AVX等等;
    不用借助这些高级指令集和其特殊寄存器,我们在高级语言范围内,也能进行
很多SIMD类似的计算;
    
问题一 : 对一个字节流的每一个数据进行右移1位
   一般的代码:  (当然,输出数组也可以是另外一个数组,下同)

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. for (int i=0;i<10000;++i)  
  3.     a[i]=a[i]>>1;  

   使用SIMD思路的代码(4路数据流同时计算):
[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. uint32* a32=(uint32*)a; //实际代码可能需要考虑内存访问对齐和边界处理问题,下同  
  3. for (int i=0;i<2500;++i){  
  4.     uint32 c=a32[i]&0xFEFEFEFE;
  5.     a32[i]=c>>1;  
  6. }  

   明白了这里的实现原理,那么对于其他右移位数/左移/双字节数据也能同理处理了;
   其他几个问题也一样可以举一反三;
   提示: 如果软件运行在64位模式,那我们就能一次处理更多的数据!

问题二 : 对一个字节流的每一个数据x,计算255-x
   一般的代码:

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. for (int i=0;i<10000;++i)  
  3.     a[i]=255-a[i];  //我见过的一个处理图像颜色取反的代码  

   使用SIMD思路的代码(4路数据流同时计算):

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. uint32* c=(uint32*)a;  
  3. for (int i=0;i<2500;++i){  
  4.     a32[i]=~a32[i];  
  5. }  


问题三 : 求两个字节流的平均字节流
   一般的代码:

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. uint8 b[10000];  
  3. for (int i=0;i<10000;++i)  
  4.     a[i]=(a[i]+b[i])>>1;//我见过的一个处理图像颜色50%混合的代码  

   使用SIMD思路的代码(2路数据流同时计算):

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. uint8 b[10000];  
  3. uint32* a32=(uint32*)a;  
  4. uint32* b32=(uint32*)b;  
  5. for (int i=0;i<2500;++i){  
  6.     uint32 c=a32[i];  
  7.     uint32 d=b32[i];  
  8.     uint32 e_1_3 =(c & 0xFF00FF00)>>1;  
  9.     uint32 e_0_2 =(c & 0x00FF00FF);  
  10.            e_1_3+=(d & 0xFF00FF00)>>1; //隔字节取数为了留下空间(0)做移位 
  11.            e_0_2+=(d & 0x00FF00FF);  
  12.     a32[i]=((e_1_3 & 0xFF00FF00)) | ((e_0_2>>1) & 0x00FF00FF);//& 0x00FF00FF为了去掉无效区域数据 
  13. }  


    如果允许结果有点小误差,也可以这样写(4路数据流同时计算):

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. uint8 b[10000];  
  3. uint32* a32=(uint32*)a;  
  4. uint32* b32=(uint32*)b;  
  5. for (int i=0;i<2500;++i){  
  6.     a32[i]=(a32[i]&0xFEFEFEFE>>1)+(b32[i]&0xFEFEFEFE>>1);  
  7. }  
 

 

  一个来源于ffmpeg的算法 (4路数据流同时计算):  (相当精彩啊)

[c-sharp] view plain copy
 print?
  1. uint8 b[10000];    
  2. uint32* a32=(uint32*)a;    
  3. uint32* b32=(uint32*)b;    
  4. for (int i=0;i<2500;++i){  
  5.     uint32 c=a32[i];    
  6.     uint32 d=b32[i];    
  7.     a32[i]=(c&d) + (((c^d) & 0xFEFEFEFE) >> 1); //http://blog.csdn.net/zhongjiekangping/article/details/6855864
  8. }  
  9.   
  10. //(还可以试试,注意最后一个bit位  (c|d)- (((c^d)&0xFEFEFEFE)>>1); )  

 


 

 

问题四 : 按指定比例混合两个字节流 (alphaBlend混合,线性插值缩放等常用的算法)
   一般的代码:
      //算法为 dst=(a*(255-s)+b*s)/255;
      //如果允许误差,可以改为 dst=((a<<8)+((int)b-a)*s)>>8;(甚至dst=a+(((int)b-a)*s>>8));

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. uint8 b[10000];  
  3. int   s=13;  //s 可能属于[0..255];  
  4. for (int i=0;i<10000;++i){  
  5.     int c=a[i];  
  6.     a[i]=((c<<8)+(b[i]-c)*s)>>8;  
  7. }  

   //如果不能有误差,这里可以用公式(x/255)==(x*32897>>23)==(x+(x>>8)+1)>>8;


   使用SIMD思路的代码(2路数据流同时计算):

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. uint8 b[10000];  
  3. int   s=13;  //s 可能属于[0..255];  
  4. uint32* a32=(uint32*)a;  
  5. uint32* b32=(uint32*)b;  
  6. int   rs=256-s;  
  7. for (int i=0;i<2500;++i){  
  8.     uint32 c=a32[i];  
  9.     uint32 d=b32[i];  
  10.     uint32 e_0_2=(c & 0x00FF00FF)*rs + (d & 0x00FF00FF)*s;       //隔字节取数为乘法预留空间(可能进位)
  11.     uint32 e_1_3=((c & 0xFF00FF00)>>8)*rs + ((d & 0xFF00FF00)>>8)*s;  
  12.     a32[i]=((e_0_2 & 0xFF00FF00)>>8) | (e_1_3 & 0xFF00FF00);  
  13. }  

 

问题四: 在字节流中查找第一个出现0值位置 (字节流的值域[0..128])   (字符串结束位置查找?)
   一般的代码:

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. for (int i=0;i<10000;++i){  
  3.     if (a[i]==0)  
  4.         return i;  
  5. }  
  6. return -1;  


   使用SIMD思路的代码(4路数据流同时计算):

[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. uint32* a32=(uint32*)a;  
  3. uint32 test=0;  
  4. int i=0;  
  5. for (;i<2500;++i){  
  6.     test=(a32[i]-0x01010101)&0x80808080;  //只要a32[i]非零,减0x01010101最高位就非零,&0x80808080就非零
  7.     if (test!=0)  
  8.         break;  
  9. }  
  10. if (test==0)  
  11.     return -1;  
  12. i*=4;  
  13. while ((test&0x80)==0){  //四字节取出后被小端存储,故++i;
  14.     ++i;  
  15.     test>>=8;  
  16. }  
  17. return i;  

     
  问题扩展: 字节流的值域[0..255]时的0查找;
   一般的代码同上,不用修改;
   使用SIMD思路的代码(4路数据流同时计算):
[c-sharp] view plain copy
 print?
  1. uint8 a[10000];  
  2. uint32* a32=(uint32*)a;  
  3. uint32 test=0;  
  4. int i=0;  
  5. for (;i<2500;++i){  
  6.     uint32 c=a32[i];  
  7.     c=((c&0xF0F0F0F0)>>4)|(c&0x0F0F0F0F);  //0-255--》0-128,每个字节高四位与第四位或,若非零,仍非零
  8.     test=(c-0x01010101)&0x80808080;  
  9.     if (test!=0)  
  10.         break;  
  11. }  
  12. if (test==0)  
  13.     return -1;  
  14. i*=4;  
  15. while ((test&0x80)==0){  
  16.     ++i;  
  17.     test>>=8;  
  18. }  
  19. return i;  

 

  当然,在有SIMD对应指令可以使用的环境下,直接用其指令一般还是比这里的模拟实现有优势的;
如果没有或者不好动用这些指令的情况下,模拟SIMD的实现还是很有速度优势的;
当你能在高级语言内熟练编写SIMD类算法,那么在真的使用SIMD指令的时候就更能得心应手了;


转自:http://blog.csdn.net/housisong/article/details/6324467

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝退货运费太贵卖家不退怎么办 顺丰文件丢失怎么办 京豆负9千多怎么办 京东押话题没了怎么办 没退运费险怎么办 本人身份证丢了怎么办 输入农行卡三次怎么办 花呗支付超限怎么办 在商场买到破衣服怎么办 vans鞋子磨脚怎么办 衣服白领子发黄怎么办 白衬衣染上黑色怎么办 车超过年检日期怎么办 用过期卫生间痒怎么办 驾照过期一个礼拜怎么办 月经总是后漏怎么办 护垫掉厕所里怎么办 健身房老板拿钱跑了怎么办 手机游戏变竖屏了怎么办 轮胎钢圈处磨损怎么办 屋里有刺鼻气味怎么办 脸上都是荧光剂怎么办 mac系统卡死了怎么办 笔记本关机要很久怎么办 mac关不了机怎么办 mac蓝屏无法开机怎么办 凤凰系统内存小怎么办 proe安装完成打不开怎么办 绿叶竹根发黑怎么办 月经期间肚子凉怎么办 来月经肚子冷怎么办 月经越来越少了怎么办 卫生巾闷出红疹子痒怎么办 开动物检疫证明怎么办 打桩机钻头掉了怎么办 临牌怎么办没有合格证 没有合格证怎么办零牌 贴了一等品瓷砖怎么办 香港过关货物被退港怎么办 换检验标过期怎么办 电动车合格证丢了怎么办