14.2.2 MMX/SSE 2实现XviD CODEC(1)
上一节对XviD CODEC的系统框架做了功能剖析和技术说明,现在开始对最底层的模块如DCT/IDCT、Q/IQ、VLC/VLD、SAD等作汇编优化和设计。第12章已经对MMX/SSE 2汇编编程、汇编指令结构等作了详细介绍。基于通用CPU进行多媒体编程,汇编语言提供了数据打包、单指令多数据处理等特点的高性能支持。
由于算法系统是C语言编程,而在使用汇编优化核心模块的时候,就涉及了混合编程技术。汇编编写的函数要被C语言调用,则在编程时要遵守一定的规则。如在汇编函数名称前加下画线'_'。
1.MMX汇编指令优化核心模块
XviD CODEC的汇编程序支持NASM汇编器,它是一个为可移植性与模块化而设计的80 86的汇编器。它支持相当多的目标文件格式,包括Linux、NetBSD/FreeBSD、a.out、ELF、COFF、微软16位的OBJ和Win32。它还可以输出纯二进制文件。它的语法设计得相当地简洁易懂,和Intel语法相似但更简单。它支持Pentium、P6、MMX、3DNow!、SSE和SSE 2指令集。
1)一般宏定义
下面的宏定义中使用了宏汇编中表达式操作符%,它把后面的文本解释为表达式,功能是取表达式的最终的值。使用这个操作符可以把表达式的值作为实参引用,而不是引用表达式文本本身。
%macro表示定义多行宏,%endmacro表示多行宏的结束。在%macro一行上宏名后面的数字1定义了宏可以接收的参数的个数,宏定义里面的%1用来引用宏调用中的第一个参数。对于一个有多个参数的宏,参数序列可以这样写:%2、%3等。%define用来定义单行宏,NASM的详细开发技术请参考Nasm中文手册。
BITS 32 ; 表明32位指令 %macro cglobal 1 ; cglobal宏定义 %ifdef PREFIX %ifdef MARK_FUNCS global _%1:function %1.endfunc-%1 %define %1 _%1:function %1.endfunc-%1 %else global _%1 ;有效定义,定义全局函数 %define %1 _%1 ;在汇编中,使用没有'_'的函数名称 %endif %else %ifdef MARK_FUNCS global %1:function %1.endfunc-%1 %else global %1 %endif %endif %endmacro |
global _myfunc define myfunc _myfunc |
;=========================================================================== ; 只读数据段(Read Only Data) ;=========================================================================== %ifdef FORMAT_COFF SECTION .rodata %else SECTION .rodata align=16 ;只读数据段,16字节(128位)对齐 %endif ALIGN 16 ;16字节对齐 mmx_one: dw 1, 1, 1, 1 ;定义的字 |
上述代码定义了16字节对齐的常量mmx_one,4个常量中每个常量长度为16比特,数据放置在只读段.rodata。
3)C语言函数transfer_8to16copy_c的功能
C语言函数transfer_8to16copy_c的功能是把像素值从8位扩展到16位。
void transfer_8to16copy_c(int16_t * const dst, const uint8_t * const src, uint32_t stride) { uint32_t i, j; for (j = 0; j < 8; j++) { for (i = 0; i < 8; i++) { dst[j * 8 + i] = (int16_t) src[j * stride + i]; } } } |
上述代码有两层循环,每次处理一个单元,即把8位的像素值扩展为16位。
MMX的寄存器是64位,这样MMX指令可以一次处理8个字节。该函数的汇编优化是transfer_8to16copy_mmx(),该函数优化的思路是把要复制的数据打包形成64位,每次存储两行,执行4次宏处理,下面是汇编优化的结果。
SECTION .text ;.text 段 cglobal transfer_8to16copy_mmx ;函数声明:_ transfer_8to16copy_mmx %macro COPY_8_TO_16 1 ;定义宏COPY_8_TO_16开始 movq mm0, [eax] ;取64bit,第一行8个像素点 movq mm1, [eax+edx] ;取64bit,第二行8个像素点 movq mm2, mm0 ;mm2=mm0 movq mm3, mm1 ;mm3=mm1 punpcklbw mm0, mm7 ;mm0的低4Byte扩展成word movq [ecx+%1*32], mm0 ;mm0的内容存到[ecx+%1*32] punpcklbw mm1, mm7 ;mm1的低4Byte扩展成word movq [ecx+%1*32+16], mm1 ;mm1的内容存到[ecx+%1*32+16] punpckhbw mm2, mm7 ;mm2的低4Byte扩展成word punpckhbw mm3, mm7 ;mm3的低4Byte扩展成word lea eax, [eax+2*edx] ;修改eax值,指向当前行的第三行 movq [ecx+%1*32+8], mm2 ;mm2的内容存到[ecx+%1*32+8] movq [ecx+%1*32+24], mm3 ;mm3的内容存到[ecx+%1*32+24] %endmacro ;定义宏COPY_8_TO_16结束 ALIGN 16 ;下面地址是16字节对齐 transfer_8to16copy_mmx: ;实际是_transfer_8to16copy_mmx,前面有define定义。 mov ecx, [esp+ 4] ; Dst ;取第一个参数 dst mov eax, [esp+ 8] ; Src ;取第二个参数 src mov edx, [esp+12] ; Stride ;取第三个参数 stride pxor mm7, mm7 ;mm7清零 COPY_8_TO_16 0 ;第一次展开宏,处理0、1行 COPY_8_TO_16 1 ;第二次展开宏,处理2、3行 COPY_8_TO_16 2 ;第三次展开宏,处理4、5行 COPY_8_TO_16 3 ;第四次展开宏,处理6、7行 ret ;函数返回 .endfunc |