资源预览内容
第1页 / 共14页
第2页 / 共14页
第3页 / 共14页
第4页 / 共14页
第5页 / 共14页
第6页 / 共14页
第7页 / 共14页
第8页 / 共14页
第9页 / 共14页
第10页 / 共14页
亲,该文档总共14页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
H.264算法的优化策略摘自http:/www.tichinese.com/Article/Video/200909/2151.html更新时间:2009-9-141 代码优化的主要方法通过代码移植能够获得在DSP上初步运行的代码,但是它由于没有考虑到DSP自身的硬件特点,不适合DSP强大的并行处理能力,因此执行效率低下,不能满足我们的实时要求,需要对其进行进一步优化.对DSP代码进行优化的手段有以下三个层次,分别是:项目级(Project)优化,算法级(algorithm)优化,指令级(instruction)优化.下面对这三种优化手段分别进行介绍. 1)项目级优化项目级优化,是对项目的整体优化,主要手段有以下几点:首先是在对整个项目进行编译链接生成DSP代码时,合理选择配置编译器选项,并针对这些参数选择,对程序进行调整和修正.其中进行的工作有: 项目编译时,通过-o3选项来调用最高级别的软件流水线优化,通过-mw选项来调用软件流水线循环反馈,从而增大软件编译成DSP代码的并行性. 项目编译时,用-pm,-o3和-mt选项来改善循环,多重循环,庞大循环体循环的性能. 只读变量声明成const型,循环计数器定义为int型,从而加大DSP代 码的并行性. 其次对程序结构进行调整,对不适合DSP执行的语句进行改写,以提高代码的并行性.例如,DSP处理器并行性很高, 能够对代码进行流水线处理,但是原始代码存在大量条件判断语句,会对流水线造成中断,不利于代码的并行处理,因此,可以采用判断提前,去除不必要的判断等 方式减少判断语句对流水线的中断.2)算法级优化算法级优化,就是利用H.264的自身特点,采用一些快速算法,在不影响编码质量的前提下,提高编码器速度,从而在速度和质量上达到一个较好的平衡.3)指令级优化上述方法无法达到要求时,就要进行指令级优化.C64x系列DSP有丰富的具有高度并行性处理能力的指令.下面介绍一些C64x系列DSP媒体处理相关指令. ADD4:加法指令,一次执行4对8位数的加法.一个寄存器有32位, 可以存放4个8位数据.计算中,两个源寄存器中的四组对应8位数据 分别相加,结果存放在目标寄存器中. AVGU4:一次执行4对8位无符号数据求平均运算.计算中,两个源 寄存器中的4组8位无符号型紧缩字求平均,结果以4个8位紧缩字的 形式存放在目标寄存器中. DOTPU4:一次执行4对8位无符号数据点乘运算.计算中,两个源寄 存器中的4组8位无符号型紧缩字对应相乘,乘积相加,所得结果存放 在32位寄存器中 SUBABS4:一次执行4对8位无符号数据求差绝对值运算.计算中,两个源寄存器中的4组8位无符号型紧缩字对应相减,差值求绝对值,所得结果以4个8位紧缩字的形式存放在目标寄存器中. LDB/LDH/LDW/LDDW:将8位,16位,32位或64位数据读入目标寄 存器中,所读取的数据在内存中是地址align(32位对齐)的数据. LDNW/LDNDW:将一个32位或64位的非对齐数据读入目标寄存器 中. STB/STH/STW/STDW:将8位,16位,32位或64位数据写入内存中, 所写入的数据在内村中是地址align(32位对齐)的数据. STNW/STNDW:将一个32位或64位的非对齐数据写入内存中. 除了这些高并行度的指令,TI还提供了丰富的算法库37,如Image/Video Processing Library图像/视频处理库(IMGLib),Digital signal processor Library数字信号处理库(DSPLib)等.这些算法库中的函数都是已经充分优化过的算法模块,而且大都提供对应的C、线性汇编和汇编源代码,并有 文档进行API介绍.所以要充分利用.2 算法关键模块的优化对模块的优化分三步进行.先认真分析代码,并进行相应的调整,例如尽量减少有判断跳转的代码,特别是在for循环 中,因为判断跳转会打断软件流水.可以用查表或者用_cmpgtu4、_cmpeq4等intrinsic来代替比较判断指令,从而巧妙地替代判断跳转语 句.同时还可以采用TI的CCS中所提供的#pragma,为编译器提供尽量多的信息.这些信息包括for循环的次数信息、数据对齐信息等.如果经过这部 分优化后还无法满足系统要求,则对这部分模块使用线性汇编来实现. 2.1 整数变换和量化整数变换和反变换的运算步骤见下表:图1 DCT与IDCT的运算步骤 整数运算和反变换的次数极多,比如,在D1格式下,4?4DCT等要做21600次,如果算上其他大小宏块的这些变换,时间消耗还是很多的,所以仍然有优化的必要.下面以整数变换为例介绍如何用线性汇编对C语言代码进行优化:1)预测残差的输入DCT和IDCT优化的关键是在数据的读和写上.读写指令是和存储器操作相关的,所以要尽量减 少其操作的次数.优化时可以用LDNW(无边界调整字(四字节)读取)和STNW(无边界调整字(四字节)存储)来代替单字节读取和存储,然后在寄存器内 部再对数据进行打包处理,这样可以大大提高速度.读入方法如下:LDNDW *A4, a1:a0 ;第一行数据LDNDW *+A4(8), a3:a2 ;第二行数据LDNDW *+A4(16), b3:b2;第三行数据LDNDW *+A4(24), b1:b0;第四行数据寄存器A4是系统指定用来保存函数返回值的.8字节的数据的读入要保存到寄存器对中.2)行变换的实现C64x里面有多种加法指令,寄存器内容可作为32位数据相加,也可作为两个16位数据相加,也可以作为四个8位数据相加.对应于上面的数据输入方法,我们采用16位相加,并使用.S功能单元.实现方法如下:加法指令ADD2 a0, b0: r_00 ;a + dADD2 a1, b1: r_01 ;ADD2 a2, b2: r_10 ;b + cADD2 a3, b3: r_11 ;减法指令SUB2 a2, b2: r _20 ; b - cSUB2 a3, b3: r_21 ; SUB2 a0, b0: r_30 ; a - dSUB2 a1, b1: r_31 ; 另外,因为在C64x指令中没有对两个16位数据同时左移的指令,同时DM642的乘法指令可 以在一个周期内完成,所以在表1中求B, D时要使用MPY2指令实现向左的移位运算.由于乘法运算后数据的位数要进行扩展,因此MPY2的结果需要放入一个寄存器对,但实际有效数据不会超过16 位,因此完成乘法后我们又把数据两两打包在一个寄存器里,以方便在下面的列变换中进行数据的并行处理.实现方法如下:MPY2 r_30, r _t,r_tl:r_ t0 ;2*(a-d) r_ t=0x00020002MPY2 r-31, r_t,r_t3:r_t2PACK2 r_t 1, r_t0, r_t0PACK2 r_t3, r_t2, r_t23)列变换的实现行变换后数据以行优先方式存放在4个寄存器对内.所以在进行列变换前要对寄存器内的数据进行转置调整,以方便我们使用上述的数据并行处理指令.第一列PACK2 .S1 a2, a0, r_ m00PACK2 .S2 b0, b2, r_ m01第二列PACKH2 .Ll a2, a0, r_ m10PACKH2 .L2 b0, b2, r ml l第三列PACK2 .S1 a3,a1,r -m20PACK2 .S2 b1,b3,r_ m21第四列PACKH2 .Ll a3,a1,r -m30PACKH2 .L2 b1,b3,r_ m31经过寄存器中数据的转置处理后,我们就可以使用与行变换类似的程序进行列变换的实现.4)输出变换结果列变换的结果即为整数变换的结果,但是输出之前必须进行第二次转置处理,使得输出数据仍然为行优先方式存储.STNDW r_mOut0l:r mOut00, *A4STNDW r_mOut1l:r mOut10, *A4(8)STNDW r_mOut2l:r mOut20, *A4(16)STNDW r_mOut3l:r mOut30, *A4(24) 5)检查生成的汇编代码并对线性汇编代码进行相应的调整,以提高效率.如,避免使用A16A31和B16B31寄存器,因为这两组寄存器需要被保护,如果被使用,编译器会花额外的时间对这些寄存器进行保护操作,这会打断流水,降低效率.线性汇编代码中用“.cproc” 和“.endproc”命令限定了需要优化器优化的代码段,“.reg”命令允许使用将要存入寄存器的数值描述名字,也就是为寄存器设定了一个标识符.寄存器A4是系统指定用来保存函数返回值的.量化(DCT)和反量化(IDCT)与整数变换相比多了if判断,如下所示:for(i = 0 ; i 0)datai = (datai * quantmf_indexi ) qbits;elsedatai = -(-(datai * quantmf_indexi) qbits);而判断指令会打断软件流水,所以应该避免使用.观察代码可知,进行移位操作的都是正数,所以优化时可以对绝对值进行操作,而把符号放在另外的存储器中,移位计算后再将符号加上去.比较线性汇编优化前后的性能,我们可以发现效果相当明显.图2 优化前后比较DCT和量化实际上是两个相关联的部分,总是成对出现,我们可以把DCT和量化一起完成,这样数据可以在寄存器中完成运算,节省了数据存储和读取时间.2.2 熵编码和解码2.2.1 查表方法的改进CAVLC编码按如下步骤进行:1编码非零系数的个数(TotalCoeffs)和TrailingOnes的个数(Coeff_token)通过查表进行,表有5个,定义在结构h264_coeff_token517*4中,选择码表的依据是当前块上面和左面块的非零系数个数(N0和N1).由这两个值计算一个参数N来查表.这就是所谓的基于上下文自适应(context adaptive).2对每个TrailingOnes的符号进行编码对于每个TrailingOnes都要用一个比特来表示它的符号 (0代表正,1代表负).符号的编码是逆序进行的,即从最高频的TrailingOnes开始编码.3对除TrailingOnes之外的非零系数的level值进行编码当前块中每个余下的非零系数的level值 (符号和绝对值) 都将按照逆序进行编码,即从最高频的系数开始到DC系数结束.编码每个level所用的查找表是依据先前己编码系数的level的绝对值来决定的(上下文 自适应).按原来的做法,此处将查7个表Level_VLC0到Level_VLC6.Level_VLC0适合编码较小的绝对值;Level_VLC1 适合编码稍大的绝对值,依此类推.现在采用一种新的查表方式,将非零系数的幅值(Levels)分成两部分:前缀(level_prefix)和后缀 (leve_suffix).前缀和后缀的求法和一个变量suffixLength有关,如下公式:变量suffixLength是基于上下文模式自适应更新的,它的更新与当前的 suffixLength的值以及已经解码好的非零系数的值(Level)有关.这样可通过更新suffixLength的值将各种非零系数的前缀都控制 在一个较小的范围内,这样既有利于码表的构建又有利于提高查
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号