G.723.1算法在DSP上的优化
1 引言
G.723.1是删组织于1996年推出的一种低码率的语音编码算法标准,也是目前该组织颁布的语音压缩标准中码率最低的一种标准。G.723.1主要用于对语音及其它多媒体声音信号的压缩,目前在一些数字音视频传输、高质量语音压缩等系统中都得到广泛应用。
2 G.723.1算法的复杂度分析
将G.723.1移植到TMS320C64xx后,就可借助TI集成开发工具CCS(Code Composer Studio)的Profile功能来评估其各个子程序或函数的执行运算量,从而把程序的优化集中在对程序性能影响最大的代码上去。
通过分析可以看出,在G.723.1的编解码算法中,码本搜索所花费的运算量是比较大的,如Find_Best(),Find_Fcbk(),Find_Acbk():另外,在LPC分析和LSP参数的计算上也有运算量比较大的,如Comp_Lpc(),Lsp_Qnt(),Lsp_Svq()。
3 代码的优化
代码优化的工作有两大目的:一是执行速度提高,实现实时;二是尽量不扩大程序体积(Code Size),使之在内存允许的范围内。显然,两者存在一定的矛盾,当今超大规模集成电路的发展使RAM资源不再是系统的瓶颈,因此该部分工作的主要任务是怎样提高执行速度。代码的优化工作主要在CCS环境中进行。优化的原则是要充分考虑C64xx处理器超长指令字、多个运算单元和深度流水线的结构特点,以及避免过多的读写内存指令和程序转移指令,充分发挥其强大的运算能力。具体方法包括(次序有先后):
3.1基本运算集的优化
G.723.1算法程序是用定点运算完成浮点运算,为了防止定点运算时可能溢出,许多运算需要进行饱和判断,为此程序专门定义了基本运算集,实现诸如饱和加法、饱和乘法、除法和移位等操作。在程序中这些操作调用相当频繁,经CCS的profile工具测试,基本运算函数集的调用占用了95%以上的CPU时间。因此,我们要从基本运算集的优化开始。在熟悉掌握C64xx指令集的前提下,分析基本运算集中各个函数完成的悉掌握C64xx指令集的前提下,分析基本运算集中各个函数完成的功能和对全局变量产生的影响,用C64xx指令取而代之或加以改编。其中包括对跳转和流水线的优化讨论、对乘积的饱和调整和全局变量OveRFlow的相关操作。
由于基本运算集以函数形式存在,两次跳转f函数的调用与返回1必不可少,将引起流水线的两次打断,表现为12个指令周期的占用。将这嵝基本运算函数集改成宏的形式,即将基本运算内嵌(inline)至lJ调用程序中,由此町以消除跳转和流水线打断带来的指令周期占用,提高执行速度。虽然这样做增加了代码长度,多占用了一些内存,但由于基本运算函数体积均较小,再经过一定的代码优化,在程序体积上的牺牲几乎町以忽略。
基本运算的函数定义在BASIC.C文件里面,如果能够对这些简单甬数进行内联指令(intrinsic)的优化,就能达到事半功倍的效果。内联指令是汇编指令的直接映射,具有很高的效率。与此同时带来的一个问题是溢出保护位Overflow的判断,这是基本函数里用来标识溢出的全局变量,它的作用等同于CSR(Control Status Register)寄存器的SAT(Saturation)位,当数据溢出时,SAT位被系统自动设置为1,所以编解码函数里对Overflow的判断可以转化成对SAT位的判断。引用CSR寄存器时需要在最开始的时候声明extem cregister volatile ansigned int CSR。
C64xx指令提供了饱和乘法指令SMPY,实现16"16位的乘法与饱和结果调整,其执行操作如下:
if(cond){
if((src 1*src2<<1)!=0x80000000)
dst=((src 1*src2)<<1);
else
dst=0x7ffffff;
}
else
nop;
将原指令中的乘法指令改为SMPY.就可以完成乘法和饱和调整两种计算,这样可以省去饱和调整3条指令。与此类似,其它的饱和运算,C64xx都提供了相应的指令实现,将普通运算指令替换为饱和运算指令,饱和结果调整部分的运算均可以省去。
3.2主程序的优化
主程序的优化手段主要采用了以下几种方法:
(1)使用内联函数(intrinsics)
内联函数是可直接映射为C64xx指令的特殊函数,它在指令前加上"_"表示。例如:
#define L_add(L_var1,L_var2) _sadd(L_var1,L_var2)
#define L_mult(var1,vat2) _smpy(var1,var2)
等,基本函数的内联优化需要对原函数的定义和内联指令都比较熟悉。
使用内联函数代替相应的C语句是一种非常简便高效的优化方法。如上面提到的饱和乘法,在C语句中。我们通常要使用两个嵌套的条件判断语句来检查结果是否溢出,而指令int_smpy(int a.int b) 则在完成乘b的运算后,再做一次饱和处理,这样一条DSP指令就可完成C语言中多条语句才能完成的计算,可以节省很多时钟周期。
(2)循环展开(loop-unrolling)
程序
- 嵌入式开发工具简介(11-09)
- 分析内核初始化时根内存盘的加载过程(init/main.c)(11-12)
- RedHatLinux新手入门教程(4)(11-12)
- 对中国Linux标准战略与战术的思考(11-12)
- 新桌面Linux标准问世促进Linux普及(11-12)
- 开源与标准(11-12)