DSP

DSP程序结构优化技巧

2019-07-13 10:00发布

       DSP的内部结构具有特殊性,如果程序的编写能够在一定程度上与DSP的结构相适应,利用C编译器的优化功能就可以在程序级的基础上大大提高算法的性能[1~5]。具体如下:        (1) 尽量使用int 类型中间变量        在图像处理程序中,数据通常为8bit的变量,而C6XDSP的内部寄存器和数据通道为32bit,在图像处理的滤波、卷积等过程中,需要较多的中间变量,如果采用8bit的中间变量存储方式,势必迫使编译器使用额外的数据调整指令,因此采用32bit的中间变量具有最好的效率。         (2) 使用移位代替除法运算        ①DSP中的移位运算具有硬件支持,由一条指令完成,而除法运算使用程序实现,比较复杂耗时。        ②DSP的浮点运算往往采用调用子函数方法,效率低,编译器无法进行软件流水优化,而采用移位运算可以代替一些定值的浮点运算,如:
for(I=0;I<1000;I++)          for(I=0;I<1000;I++)
{                                       {
     b+=35*0.325;                  b+=((35*333)>>10);
}                                        }        以上两条程序完成同样的功能,但是第一条含有浮点数的乘法,运行时间为222 775;第二条只有定点数的乘法和移位运算,运行时间仅为881,可见效率差距非常大。        (3) 使用C6x内联函数        C6x编译器内部提供了许多指令,可以在单周期内完成许多复杂的函数功能,从而提高了代码的运行速度。如:
 “饱和加法”:
int sadd(int a, int b)
{
    int result;
    result = a + b;
    if (((a ^ b) & 0x80000000) == 0)
    {
        if ((result ^ a) & 0x80000000)
        {
            result = (a < 0) ? 0x80000000 : 0x7fffffff;
        }
    }
    return (result);
}        这样一个复杂的算法可以用一条内联函数_sadd( a,b)实现。         (4) 使用32bit数据类型访问16bit数据        由于C6x系列DSP具有32位的寄存器和内部通道,在操作16bit的数据如short类型时,往往浪费一半的寄存器空间和通道带宽。为了充分利用这些资源,编译器设置了一些内联函数同时对两个16bit的数据进行操作,如_add2、_sub2。
short in1[]、 short in2[] 是两个short类型的数组,具有N项,以下是将两者对应项相加的操作。
for (i = 0; i < (N/2); i++)
_amem4(&sum[i]) = add2(_amem4_const(&in1[i]), _amem4_const(&in2[i]));
程序中,_amem4_const、_amem4将in1、in2和sum进行了32bit的对齐操作,然后同时进行两个short数据的加法运算和读取存储运算,从而提高了运算效率。        (5) 使用restrict关键字消除内存关联        为提高代码效率,C6x编译器总是尽可能多地安排指令并行,而指令能否并行取决于指令之间的相关性。对于编译器而言,内存读写指令之间是否独立是很难判断的,如下列程序:
void vecsum(short *sum, short *in1, short *in2, unsigned int N)
{
    int i;
    for (i = 0; i < N; i++)
    sum[i] = in1[i] + in2[i];
}        程序中,sum的存储对于in1、in2的读取地址产生影响,只有等到sum存储完毕以后,才可以再次进行in1、in2的读取操作。这个问题叫做“别名问题”,因为sum可能和in1是一个地址,使其无法将读取数据和写结果的操作并行起来。
为了让编译器放心地将读取源数据和写结果数据两者并行操作,可以利用restrict变量申明当前数组名(或指针)是指向这段内存的惟一变量,如下列程序所示:
void vecsum(short *sum, short * restrict  in1, short * restrict in2, unsigned int N)
{
    int i;
    for (i = 0; i < N; i++)
    sum[i] = in1[i] + in2[i];
}
这样就可以消除以上的内存相关性,提高流水线效率。         (6) 软件流水优化        在程序的优化中,循环部分往往是最耗时的步骤,软件流水就是一种用来优化循环步骤的,使其循环内部的指令尽可能地并行执行。在C6201编译器中选择-o2或者-o3开关就可以打开编译器的软件流水优化功能。        形成软件流水的有效方法:在编译器选项中选定了-o2或者-o3选项,编译器就可以自动作出软件流水的优化。形成高效的软件流水,可以使用MUST_ITERATE给定循环变量信息和循环展开。形成软件流水应避免:①软件流水优化只在最内层循环进行。②循环体内部代码太多。③循环体内部代码太复杂。        在定点DSP中,浮点数的运算都是用子函数软件实现的,所以在定点DSP的程序循环中,如果由浮点数运算,就无法形成软件流水。解决的方法是手工进行浮点数的定标工作。对于已经确定操作数的浮点运算,如果运算结果可以是整数,就可以使用乘法加移位的方法实现乘除运算。如35×0.25可以转化成(35×1)>>2。 参考文献: [1]  田元,叶秀清.实时图像处理中的DSP优化编程,电子技术,2000,(10):43-46.
[2]  Texas Instruments Incorporated.TMS320C6000 Programmer’s Guide, 2001.
[3]  Texas Instruments Incorporated. TMS320C6000 optimizing compiler user’s guide, 2001.
[4]  徐盛. TMS320C6201数字信号处理器在图像处理中的应用.电子技术应用,1999,25(1).
[5]  WAMES P. Hunt Engineering Incorporated. Developing a complex real time system with HERON and HEART Rev1.0. 2002.