MMX和SSE的运用

Intel的MM技术是对Intel体系结构(IA)指令集的扩展。该技术使用了单指令多数据技术(SIMD)技术,以并行方式处理多个数据元素。MMX?指令集增加了57条新的操作码和一个新的64位四字数据类型,增加了八个新的64位MMX寄存器,每个寄存器可按名称MM0-MM7直接访问。这意味着我们可以在一个寄存器里最多存8个数据(最小单位字节)。 
SSE系列是MMX的超集,直到SSE2才跟MMX有本质的区别,添加了对64位双精度浮点数的支持,以及对整型数据的支持,也就是说这个指令集中所有的MMX指令都是多余的了,同时也避免了占用浮点数寄存器。这个指令集还增加了对CPU的缓存的控制指令。新的SSE寄存器命名为xmm0~xmm7,每个128位。 
对H264的优化主要用到了MMX和SSE2。举个例子,在H264解码端断的IDCT和QUANT里,要操作的数是一个4X4的矩阵,很适合拿来优化。 IDCT计算公式:
MMX和SSE的运用 - 菜包 - falloutmx的博客
  
 

其中,“?Ei已在前面的反量化中完成,其结果为 Wr,所以这里只进行以下运算:

 
MMX和SSE的运用 - 菜包 - falloutmx的博客
在反变换过程中,可以分成两次一维变换,使用了蝶形算法来简化运算,。 原函数就不给了,直接看优化完成后的 优化代码: 

memcpy(&m7,(&img->cof[i0][j0][0][0]),sizeof(short)*16);         //如果不能采用数组形式的话,输入输出将十分的麻烦。只能通过移位指令,浪费大量的时间。 
_mm_prefetch(&m7,_MM_HINT_NTA);       //  _mm_prefetch将数据预存到cache里,一次存一个cache line的数据,_MM_HINT_NTA表示存到离CPU最近的cache里(既L1 cache)。在操作开始前执行_mm_prefetch可以避免由于读未命中产生的大量延时。

 _asm{ 
movq mm0,[m7] // movq尽可能的在一起, 前提是在一起的mov不要使用一样的mmx寄存器
 movq mm1,[m7+8] //2.连续的读指令可以配对,当指令可以配对的时候,一个时钟周期可以同时处理这两条指令(U管道与V管道)。最好避免AGI延迟
 movq mm2,[m7+16]
 movq mm3,[m7+24] //通过MOVQ指令一次处理四个向量元素,并通过PADDW指令一次完成四个加法。将使速度提高四倍
paddw mm2,mm0             //m6[0]=(m5[0]+m5[2]);每个指令前都有个“P”开头。这个“P”的意思就是——Packet,就是说每个寄存器里的数据都是个数据包,而不是一个数据。指令最后的w表示按数据包里每个数据的大小是“word”, addw既   paddw mm0,mm0 psubw mm0,mm2 m6[1]=(m5[0]-m5[2]);
 movq mm4,mm1 psraw mm4, 1 (m5[1]>>1) //基本的优化常识,乘2和除2操作用移位代替。如果有必要使用常数,那么使用立即数通常比把常数读取到寄存器更有效。但是如果同一个立即数被多次引用,应把常数先取到一个寄存器,这种方法更快 
psubw mm4,mm3                  // m6[2]=(m5[1]>>1)-m5[3];
 psraw mm3, 1                       // (m5[3]>>1) //由 于只有一个mmx移位寄存器, 移位分组指令 是不能配对的 
paddw mm1,mm3               // m6[3]=m5[1]+(m5[3]>>1); 
paddw mm4,mm0               //     img->m7[0][j]=m6[0]+m6[3]; 
paddw mm0,mm0 
psubw mm0,mm4               //          img->m7[3][j]=m6[0]-m6[3]; 
paddw mm1,mm2              //          img->m7[1][j]=m6[1]+m6[2]; 
paddw mm2,mm2 
psubw mm2,mm1               //          img->m7[2][j]=m6[1]-m6[2]; 
//矩阵倒换,拆包和打包指令可以用于矩阵行列倒换 
m5[j]=img->m7[i][j];
MMX和SSE的运用 - 菜包 - falloutmx的博客

movq mm5,mm0
movq mm3,mm1

PUNPCKlwd mm1,mm4 
PUNPCKhwd mm3,mm4 
PUNPCKlwd mm0,mm2 
PUNPCKhwd mm5,mm2 

movq mm6,mm1 
movq mm7,mm3 
PUNPCKldq mm1,mm0 
PUNPCKhdq mm6,mm0
 PUNPCKldq mm3,mm5 
PUNPCKhdq mm7,mm5 

//第二次一维变换 ,跟第一次完全一样 
movq mm4,mm6 
paddw mm3,mm1             // m6[0]=(m5[0]+m5[2]); 
paddw mm1,mm1 
psubw mm1,mm3             // m6[1]=(m5[0]-m5[2]); 
psraw mm4,1 
psubw mm4,mm7             // m6[2]=(m5[1]>>1)-m5[3]; 
psraw mm7,1
paddw mm6,mm7             //m6[3]=m5[1]+(m5[3]>>1); 
paddw mm4,mm1             // img->m7[0][j]=m6[0]+m6[3]; 
paddw mm1,mm1 
psubw mm1,mm4             // img->m7[3][j]=m6[0]-m6[3]; 
paddw mm6,mm3             // img->m7[1][j]=m6[1]+m6[2]; 
paddw mm3,mm3 
psubw mm3,mm6             // img->m7[2][j]=m6[1]-m6[2];
movq [m7+24],mm3 //在优化的时候,数据对齐是很关键的。比如说在这个地方,如果内存8位对齐的话,是一个64位写,否则2个32位写。 movq [m7+16],mm1 
movq [m7+8],mm4 
movq [m7],mm6 EMMS // 由于MMX用的是浮点寄存器,使用EMMS指令清MMX寄存器并置浮点标志字的值为空(即为全1)。该指令应在代码段结束时插入,以避免执行浮点代码时产生浮点栈的溢出错误。这条指令将耗费20-50个时钟周期。 



像素恢复操作:
 img->m7[i][j] =max(0,min(img->max_imgpel_value,( img->m7[i][j] +((long)img->mpr[i+ioff][j+joff] <<6)+32)>>6));
 img->m7[i][j1]=max(0,min(img->max_imgpel_value,( img->m7[i][j1]+((long)img->mpr[i+ioff][j1+joff]<<6)+32)>>6)); 
MMX里没有max和min相关的指令操作,因此用SSE2进行优化。Intel提供了intrinsics库让我们写SSE代码更方便快速,但是这样会比汇编代码要慢,因为它不在乎寄存器的分配和重新使用。
举个例子: 
temp_m70_sse=_mm_slli_epi16(temp_m70_sse,6); 
_asm{ 
movdqa xmm0,xmmword ptr [temp_m70_sse] 
psllw xmm0,6 
movdqa xmmword ptr [temp_m70_sse],xmm0 

temp_m70_sse=_mm_add_epi16(temp_m70_sse,temp_32_sse); 
_asm{ 
movdqa xmm0,xmmword ptr [temp_m70_sse] 
movdqa xmm1,xmmword ptr [temp_32_sse] 
paddw xmm0,xmm1 
movdqa xmmword ptr [temp_m70_sse],xmm0 

可以看到,运用这些指令的时候,每条指令都被单独的处理,没有考虑前后相关性,导致数据多次进行存取,降低了速度,有可能比原来的C语言更慢。 剩下没什么好说的,优化后的代码如下:
 temp_32_sse=_mm_set1_epi16(32); 
m70_sse=_mm_set_epi16(i16a[13],i16a[9],i16a[5],i16a[1],i16a[12],i16a[8],i16a[4],i16a[0]); //由于要处理的数据在内存上不是连续的,因此把这些数据读到xmm寄存器里会耗费大量时间,编译后看反汇编的的代码就知道了。 m71_sse=_mm_set_epi16(i16a[15],i16a[11],i16a[7],i16a[3],i16a[14],i16a[10],i16a[6],i16a[2]); temp_255_sse=_mm_set1_epi16(img->max_imgpel_value); temp_zero_sse=_mm_set1_epi16(0); temp_m70_sse=_mm_set_epi16(img->mpr[i1_ioff][3+joff],img->mpr[i1_ioff][2+joff],img->mpr[i1_ioff][1+joff],img->mpr[i1_ioff][joff], img->mpr[i0_ioff][3+joff],img->mpr[i0_ioff][2+joff],img->mpr[i0_ioff][1+joff],img->mpr[i0_ioff][joff]); 

temp_m71_sse=_mm_set_epi16(img->mpr[i3_ioff][3+joff],img->mpr[i3_ioff][2+joff],img->mpr[i3_ioff][1+joff],img->mpr[i3_ioff][joff], img->mpr[i2_ioff][3+joff],img->mpr[i2_ioff][2+joff],img->mpr[i2_ioff][1+joff],img->mpr[i2_ioff][joff]);

 _asm{
 movdqa xmm0,xmmword ptr [temp_m70_sse] 
movdqa xmm1,xmmword ptr [temp_32_sse] 
movdqa xmm2,xmmword ptr [temp_m71_sse] 
psllw xmm0,6 
movdqa xmmword ptr [temp_m70_sse],xmm0
 paddw xmm0,xmm1 
movdqa xmmword ptr [temp_m70_sse],xmm0
 psllw xmm2,6 
paddw xmm2,xmm1 
movdqa xmm1,xmmword ptr [m70_sse] 
movdqa xmm3,xmmword ptr [m71_sse] 
paddw xmm0,xmm1 
paddw xmm2,xmm3 
psraw xmm0,6 
psraw xmm2,6 
movdqa xmmword ptr [temp_m70_sse],xmm0 
movdqa xmmword ptr [temp_m71_sse],xmm2 
movdqa xmm1,xmmword ptr [temp_255_sse] 
movdqa xmm3,xmmword ptr [temp_zero_sse] 
pminsw xmm0,xmm1 
pmaxsw xmm0,xmm3 
pminsw xmm2,xmm1 
pmaxsw xmm2,xmm3 
packuswb xmm0,xmm2 
movdqa xmmword ptr [temp_m70_sse],xmm0 //SSE不需要EMMS操作
 } 
_mm_storeu_si128(img_m7,temp_m70_sse);//sse使用专用的结构_m128i,因此最后需要存到我们自己的数组里
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值