搜档网
当前位置:搜档网 › H264算法的优化策略1

H264算法的优化策略1

没有公告 加入收藏 设为首页 联系站长 .?网站首页?.?资讯中心?.?技术文库?.?在线学院?.?会员下载?.?电子商城?.?助您选购?.?邮购需知?.?技术论坛?.?TI OS高级应用?. |?技术文库首页?|?TI DSP?|?TI MCU?|?TI 综合应用?|?TI 解决方案?|?视频编码?|?音频编码?| 您现在的位置:?TIchinese?>>?技术文库?>>?视频编码?>>?正文 [组图]H.264算法的优化策略1 组图]H.264算法的优化策略1 算法的 ★★★ 【字体:小 大】
H.264算法的优化策略1 H.264算法的优化策略1 算法的
作者:Free 文章来源:Free 点击数: 18 更新时间:2009-9-14
1 代码优化的主要方法 通过代码移植能够获得在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),Dig ital 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 + d ADD2 a1, b1: r_01 ; ADD2 a2, b2: r_10 ;b + c ADD2 a3, b3: r_11 ;

减法指令 SUB2 a2, b2: r _20 ; b - c SUB2 a3, b3: r_21 ; SUB2 a0, b0: r_30 ; a - d SUB2 a1, b1: r_31 ; 另外,因为在C64x指令中没有对两个16位数据同时左移的指令,同时DM642的乘法指令可以在一个周期内完成,所以在表1中求B, D时要使 用MPY2指令实现向左的移位运算。由于乘法运算后数据的位数要进行扩展,因此MPY2的结果需要放入一个寄存器对,但实际有效数据不会超过1 6位,因此完成乘法后我们又把数据两两打包在一个寄存器里,以方便在下面的列变换中进行数据的并行处理。实现方法如下: MPY2 r_30, r _t,r_tl:r_ t0 ;2*(a-d) r_ t=0x00020002 MPY2 r-31, r_t,r_t3:r_t2 PACK2 r_t 1, r_t0, r_t0 PACK2 r_t3, r_t2, r_t2 3)列变换的实现 行变换后数据以行优先方式存放在4个寄存器对内。所以在进行列变换前要对寄存器内的数据进行转置调整,以方便我们使用上述的数据并 行处理指令。 第一列 PACK2 .S1 a2, a0, r_ m00 PACK2 .S2 b0, b2, r_ m01 第二列 PACKH2 .Ll a2, a0, r_ m10 PACKH2 .L2 b0, b2, r ml l 第三列 PACK2 .S1 a3,a1,r -m20 PACK2 .S2 b1,b3,r_ m21 第四列 PACKH2 .Ll a3,a1,r -m30 PACKH2 .L2 b1,b3,r_ m31 经过寄存器中数据的转置处理后,我们就可以使用与行变换类似的程序进行列变换的实现。 4)输出变换结果 列变换的结果即为整数变换的结果,但是输出之前必须进行第二次转置处理,使得输出数据仍然为行优先方式存储。 STNDW r_mOut0l:r mOut00, *A4 STNDW r_mOut1l:r mOut10, *A4(8) STNDW r_mOut2l:r mOut20, *A4(16) STNDW r_mOut3l:r mOut30, *A4(24) 5)检查生成的汇编代码并对线性汇编代码进行相应的调整,以提高效率。如,避免使用A16~A31和B16~B31寄存器,因为这两组寄存器需要 被保护,如果被使用,编译器会花额外的时间对这些寄存器进行保护操作,这会打断流水,降低效率。

线性汇编代码中用“.cproc” 和“.endproc”命令限定了需要优化器优化的代码段,“.reg”命令允许使用将要存入寄存器的数值描述名 字,也就是为寄存器设定了一个标识符。寄存器A4是系统指定用来保存函数返回值的。 量化(DCT)和反量化(IDCT)与整数变换相比多了if判断,如下所示: for(i = 0 ; i < 16 ; i ++) { if (data[i] > 0) { data[i] = (data[i] * quant[mf_index][i] ) >> qbits; } else { data[i] = -(-(data[i] * quant[mf_index][i]) >> qbits); } } 而判断指令会打断软件流水,所以应该避免使用。观察代码可知,进行移位操作的都是正数,所以优化时可以对绝对值进行操作,而把符 号放在另外的存储器中,移位计算后再将符号加上去。 比较线性汇编优化前后的性能,我们可以发现效果相当明显。
图2 优化前后比较 DCT和量化实际上是两个相关联的部分,总是成对出现,我们可以把DCT和量化一起完成,这样数据可以在寄存器中完成运算,节省了数据 存储和读取时间。 2.2 熵编码和解码 2.2.1 查表方法的改进 CAVLC编码按如下步骤进行: 1.编码非零系数的个数(TotalCoeffs)和TrailingOnes的个数(Coeff_token) 通过查表进行,表有5个,定义在结构h264_coeff_token[5][17*4]中,选择码表的依据是当前块上面和左面块的非零系数个数(N0和N 1)。由这两个值计算一个参数N来查表。这就是所谓的基于上下文自适应(context adaptive)。 2.对每个TrailingOnes的符号进行编码 对于每个TrailingOnes都要用一个比特来表示它的符号 (0代表正,1代表负)。符号的编码是逆序进行的,即从最高频的TrailingOnes开始 编码。 3.对除TrailingOnes之外的非零系数的level值进行编码 当前块中每个余下的非零系数的level值 (符号和绝对值) 都将按照逆序进行编码,即从最高频的系数开始到DC系数结束。编码每个level 所用的查找表是依据先前己编码系数的level的绝对值来决定的(上下文自适应)。按原来的做法,此处将查7个表Level_VLC0到Level_VLC6。Lev el_VLC0适合编码较小的绝对值;Level_VLC1适合编码稍大的绝对值,依此类推。现在采用一种新的查表方式,将非零系数的幅值(Levels)分

成两部分:前缀(level_prefix)和后缀(leve_suffix)。前缀和后缀的求法和一个变量suffixLength有关,如下公式:
变量suffixLength是基于上下文模式自适应更新的,它的更新与当前的suffixLength的值以及已经解码好的非零系数的值(Level)有关。 这样可通过更新suffixLength的值将各种非零系数的前缀都控制在一个较小的范围内,这样既有利于码表的构建又有利于提高查表的速度,更 新后的算法仅需要一个level_prefix的码表,码表如下。
图3 level_prefix码表 文章录入:admin 责任编辑:admin 上一篇文章: H.264算法的优化策略2 下一篇文章: 没有了 【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口】 最新热 最新热点 没有热门文章 最新推荐 没有推荐文章 相关文章 H.264算法的优化策略2 H.264编码核心算法 H.264/AVC技术细节 怎么区分H.264视频流的I?fra… 视频分辨率大全 H.264?视频RTP负载格式 MPEG-4与H.264标准的区别 H.264学习历程 H.264?NAL层解析 H.264压缩算法详解
网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!) 评论: 没有任何评论 | 设为首页 | 加入收藏 | 联系站长 | 友情链接 | 版权申明 | 网站公告 | 管理登录?|?
湘ICP备06016957号 站长:TIchinese


相关主题