优化编程的几个方法[转贴]

来源:互联网 发布:写日本战国的小说知乎 编辑:程序博客网 时间:2024/05/18 11:22
优化编程的几个方法[转贴]

使用TMS320C6000进行程序设计时,首先的感觉是汇编指令集太小了。C6000在设计时采用了一种类RISC机的结构,运算速率特别快,但是指令集却非常简单。象DSP算法中常用的乘加指令、循环操作指令等,在C54x和C3x中两条指令就可以完成的功能,而在C6000中却需要一个循环体,所以它的程序设计一般比较复杂。要想充分发挥C6000的运算能力,必须从它的硬件结构出去,最大限度地利用八个功能单元,使用软件流水线,尽量让程序无冲突的并行执行。

并行处理的长处在于,在处理彼此之间没有承接关系的运算时,在CPU资源允许的情况下可以并行完成。但对于前后有承接关系或者判断、跳转频繁的情况,就无法发挥并行的优势。一般循环体都满足并行处理的条件,并且循环体往往是程序中耗时最长的地方。因此进行C6000应用开发时应将优化重点放在循环体上。为了降低开发难度,C6000提供了很多在高级语言(如ANSI C)一级对程序进行优化的方法。在应用满足实时性处理要求时,应尽量采有这种方法。但是这种方法的效率比较低,C语言优化最好的例子是点乘,这种循环使用C语言进行优化可以百分之百地的利用CPU资源,程序的并行性达到最好。但是我们在做20点的点乘时发现它耗时是汇编语言程序的3倍。所以如果系统的实时性要求比较高,就不能使用这种优化方法了。

这时可以考虑使用线性汇编语言进行开发。线性汇编语言是TMS320C6000中独有的一种编程语言,介于高级语言和低级语言之间。因为在用手写汇编语言进行应用开发时,开发者除了要精通C6000的指令系统之外,还必须为指令分配功能单元、考虑指令的延这和功能单元之间的配合以及合理分配使用32个寄存器,才能写出高效的并行指令,发挥C6000的威力。上面任何一个方面出现问题,都会严重影响算法的效率。

线性汇编语言的指令系统和汇编语言的指令系统完全相同,但是它有自己的汇编优化器指令系统,用于和汇编性汇编语言时不需要考虑指令的延时、寄存器的使用和功能单元的分配,完全可以按照高级语言的方式进行编写。当然由于它不是高级语言,有许多编程的限制。例如,在优化循环体时,不能使用跳转到循环体之外的跳转指令;另外计数顺只能使用减计数,如果使用加计数,优化器将不能工作等等。但总的说来,它的代码效率远远高于高级语言,而且开发难度和开发周期比汇编语言要小得多。

在实际开发过程中需要具体情况具体分析,选择一种高效、快捷的开发方法。以下结合应用开发中的几个模块来简述我们使用的优化方法。

1 使用汇编语言进行

使用汇编语言进行并行编程难度比较大。但在有些情况下,程序中数据有非常强的承接关系,并且该程序体逻辑关系清楚,使用的寄存器不超过32个,这时直接使用汇编语言实现,效率会更高。另外,有些使用C语言比较难实现的运算函数,在C6000的汇编指令集中可能有专用DSP指令,这时就可以直接使用汇编语言实现。

使用汇编语言进行编程时特别需要注意的是C6000指令的延迟情况,有些指令并不是立刻就能得到结果。C6000指令集中有延迟的指令如表1所示。

表1 C6000的有延迟指令

指令类型 延时长度(时钟周期) 
Branch(跳转) 5 
Load(从存储器中取数据) 4 
Multiply(乘法) 1 
例1 32位归一化函数morm_1()

short morm_1(long L_var1)

{short var_out;

if (L_var1= = 0L){

var_out = (short)0;

}

else {

if (L_var1= = (logn)0xffffffffL{

var_out = (short)31;

}

else {

if (L_var1< 0L) {

L_var1 = ~L_var1;

}

for(var_out=(short)0;L_var1<(long)0x40000000L;

var_out++){

L_var1 <<= 1L;

}}}

return(var_out);

}

使用汇编语言进行优化;

.global norm_1

_norm1:

B B3

CMPEQ 0,A4,B0

[!B0] NORM A4,A4

NOP 3

消耗时间(时钟周期):C语言norm_1()为723;汇编语言为11。

2 使用线性汇编语言重写整个函数

对于某些以循环体为主的函数可以使用线性汇编语言重写整个函数。使用汇编优化器进行优化之后,效率是非常高的。

下面例子是算法中计算帧能量的函数,其中包含两个单循环体。进行优化时,首先要确定循环的次数。对于循环次数是变量的情况,优化器不进行并行优化;其次尽量减少数据存取次数,例如以32位存取指令对16位数据进行存取,可以节省一增的存取周期。仔细观察C代码,会发现两次循环次数相同。第二个循环要用到第一个循环的结果,因此可以将两个循环合并在一起,这样就避免了在第二个循环中再从存储器中取结果,减少了一半的Load操作。

Long Comp_En(short *Dpnt)

{ int i;

long Rez;

short Temp[60];

for (i=0;i<60;i ++) Temp [i] = shr(Dpnt[i],(short) 2);

Rez=(long) 0;

for (i=0; i <60; i ++) Rez=L_mac(Rez,Temp[i],Temp[i]);

return Rez;

}

相应的线性汇编程序如下:

.global _Comp_En ;函数名定义,对c变量前加_

_Comp_En .cproc Dpnt;函数头定义,Dpnt是参数

.reg Rez,Rez1,Rez2,1 ;寄存器定义,不必考虑实际的寄存器分配

.reg t1,t2,x1,c1,m1,m2

zero Rez

zero Rez1

zero Rez2

mv Dpnt,c1

mvk 30,i ;确定循环次数。因为用LDW代替LDH,循五环次数减少一半。

loop1 .trip 30

ldw *c1++,x1

sh1 x1,16,t1

shr t1,2,t1

shr x1,2,t2 ;将两个循环合在一起,又减少了一半的从内存取数据的时间。

smpyh t1,t1,m1

smpyh t2,t2,m2

sadd Rez1,m1,Rez1

sadd Rez2,m2,Rez2

[i] sub i,1,i ;循环计数器从30递减

[i] b loop1

sadd Rez1,Rez2,Rez

.return Rez

.endproc

消耗时间(时钟周期):C语言为32971;线性汇编语言为93。

3 使用线性汇编改写复杂函数中的循环体

当函数的逻辑关系复杂,判断、跳转、函数调用情况特别多时,上面方法的效果就会在打折扣。这时可以使用线性汇编将其中的循环部分改写成一个函数,以优化后的函数调用代替环部分,而不是优化整个复杂函数
原创粉丝点击