14.2.2 MMX/SSE 2实现XviD CODEC(2)
经过分析,上述汇编函数的显示定义如下,可以看出汇编函数名称前必须加"_",以保证能够被C语言调用。传入的参数在堆栈中,使用ESP指针加偏移量读取。
;--------------------------------------------------------------------------- ; void transfer_16to8copy_mmx(uint8_t * const dst, ; const int16_t * const src, ; uint32_t stride); ;--------------------------------------------------------------------------- SECTION .text global _transfer_8to16copy_mmx ALIGN 16 _transfer_8to16copy_mmx: ;汇编指令 ;汇编指令 ret .endfuncm32
|
上述代码是函数transfer_16to8copy_mmx的汇编实现。汇编中函数名称前加了下画线"_"。使用global关键字声明该函数为全局函数,使得C语言能调用该函数。
其他有的核心模块也使用了MMX汇编指令作优化。开发过程与transfer_ 8to16copy_mmx类似。详情参考xx_mmx.asm文件,这些模块的优化使用了MMX指令。
2.SSE 2汇编指令优化核心模块
还有一部分核心模块使用了SSE 2汇编指令作优化。SSE 2汇编语言的数据寄存器是128位,共8个,即XMM0~XMM7。这样SSE指令可以一次处理16字节,相比MMX指令的一次处理8字节,效率提高了一倍。
MMX和SSE 2的一般宏定义相同,常量同样放在.rodata段下,地址16字节对齐。
下面以sad模块的SSE 2汇编优化为案例,分析SSE 2汇编指令优化过程,优化思路是每次处理宏块的2行,执行8次宏处理。
sad16的C语言实现如下。
uint32_t sad16_c(const uint8_t * const cur, const uint8_t * const ref, const uint32_t stride, const uint32_t best_sad) { uint32_t sad = 0; uint32_t i, j; uint8_t const *ptr_cur = cur; //当前帧指针 uint8_t const *ptr_ref = ref; //参考帧指针 for (j = 0; j < 16; j++) { //外循环 for (i = 0; i < 16; i++) { //内循环 int pixel = (ptr_cur[i] - ptr_ref2[i]); //对应位置相间 sad += abs(pixel); //累加绝对值 } ptr_cur += stride; //指向下一行 ptr_ref += stride; //指向下一行 } return sad; //返回sad值 }
|
上述程序是计算对应块的sad值,两层循环,每次只计算一个像素点的差值,先采用SSE的指令优化该函数。下面代码是该函数的SSE优化结果。
;--------------------------------------------------------------------------- ; uint32_t sad16_sse2 (const uint8_t * const cur, <- assumed aligned! ; const uint8_t * const ref, ; const uint32_t stride, ; const uint32_t /*ignored*/); ;--------------------------------------------------------------------------- %macro SAD_16x16_SSE2 0 ;定义宏 movdqu xmm0, [edx] ;为16字节对齐读取,=>xmm0 movdqu xmm1, [edx+ecx] ;为16字节对齐读取,=>xmm1 lea edx,[edx+2*ecx] ;修改edx movdqa xmm2, [eax] ;16字节对齐读取,=>xmm2 movdqa xmm3, [eax+ecx] ;16字节对齐读取,=>xmm3 lea eax,[eax+2*ecx] ;修改eax psadbw xmm0, xmm2 ;8字节的sad值,结果放置在低16位 paddusw xmm6,xmm0 ;8个无符号16位饱和相加 psadbw xmm1, xmm3 ;8个字节的sad值,结果放置在低16位 paddusw xmm6,xmm1 ;8个无符号16位饱和相加 %endmacro ;宏定义结束 ALIGN 16 ;16字节对齐 sad16_sse2: ;函数声明,_sad16_see2 mov eax, [esp+ 4] ; cur (assumed aligned) ;取第一个参数,cur是16字节对齐 mov edx, [esp+ 8] ; ref ;取第二个参数,ref对齐不限制 mov ecx, [esp+12] ; stride ;取第三个参数 pxor xmm6, xmm6 ; accum ;xmm6 = 0 SAD_16x16_SSE2 ;宏展开,处理0、1行 SAD_16x16_SSE2 ;宏展开,处理2、3行 SAD_16x16_SSE2 ;宏展开,处理4、5行 SAD_16x16_SSE2 ;宏展开,处理6、7行 SAD_16x16_SSE2 ;宏展开,处理8、9行 SAD_16x16_SSE2 ;宏展开,处理10、11行 SAD_16x16_SSE2 ;宏展开,处理12、13行 SAD_16x16_SSE2 ;宏展开,处理14、15行 pshufd xmm5, xmm6, 00000010b ;根据00000010b重排xmm6 paddusw xmm6, xmm5 ;8个无符号16位饱和相加 pextrw eax, xmm6, 0 ;扩展字,前面补零 ret .endfunc |
在上述代码中,宏SAD_16x16_SSE 2处理16 16宏块的2行,16个的单字节的像素存储SSE的一个128位寄存器XMM中。调用8次宏,完成整个宏块的处理。
不论是MMX还是SSE 2汇编指令,优化的根本思路都是数据打包,单指令处理多数据源(SIMD)。
【责任编辑:
云霞 TEL:(010)68476606】