打印
[应用相关]

SP程序结构优化技巧

[复制链接]
580|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
_gege|  楼主 | 2019-11-2 18:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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。

使用特权

评论回复
沙发
Mozarts| | 2019-11-2 18:38 | 只看该作者
这样运行效率能有明显的提升?

使用特权

评论回复
板凳
Listate| | 2019-11-2 18:40 | 只看该作者
通过编译器选项设置-o2及以上  。。

使用特权

评论回复
地板
Edisons| | 2019-11-2 19:02 | 只看该作者
DSP程序生成的可执行文件按段的方式进行存储,不同的段存储于不同的区域,而这会在影响内核访存的基础上进一步影响程序执行的效率  

使用特权

评论回复
5
laozhongyi| | 2019-11-2 19:05 | 只看该作者
其实可通过配置DSP/BIOS实现。  

使用特权

评论回复
6
wanglaojii| | 2019-11-2 19:08 | 只看该作者
使用这些库,能够明显提高DSP的运算效率。  

使用特权

评论回复
7
bbapple| | 2019-11-2 19:10 | 只看该作者
对于DSPc6000系列,可以采用IQmath库函数  

使用特权

评论回复
8
handleMessage| | 2019-11-2 19:12 | 只看该作者
DSP算法优化有哪些参考资料  dsp要用到汇编语言吗  ?

使用特权

评论回复
9
zhouhuanの| | 2019-11-2 19:14 | 只看该作者
可以,DSP算法关键在于移植和优化 。

使用特权

评论回复
10
feiqi1| | 2019-11-2 19:16 | 只看该作者
选择C还是选择ASM进行DSP编程  

使用特权

评论回复
11
androidbus| | 2019-11-2 19:18 | 只看该作者
对程序编译工具进行合理配置  !!

使用特权

评论回复
12
litengg| | 2019-11-2 19:20 | 只看该作者
楼主最好给一些程序设计方面的资料。  

使用特权

评论回复
13
qiangweii| | 2019-11-2 19:22 | 只看该作者
预处理指令#pragma MUST_ITERATE()的使用,将大量的一种循环以及内循环为常数次的二重循环展开。

使用特权

评论回复
14
shashaa| | 2019-11-2 19:23 | 只看该作者
将浮点运算转换为定点运算计算。  

使用特权

评论回复
15
xia00| | 2019-11-2 19:25 | 只看该作者
可以优化设置各个段的存放位置,提升程序执行效率  。

使用特权

评论回复
16
hfdy01| | 2019-11-2 19:26 | 只看该作者
代码指令的并行是软件自动优化得到的 。。。

使用特权

评论回复
17
CallReceiver| | 2019-11-2 19:27 | 只看该作者
需要注意的是在使用IQmath库时需要对使用IQ数据类型的精度和支持数据范围进行权衡,从而获得较好的效果。

使用特权

评论回复
18
boy1990| | 2019-11-2 19:28 | 只看该作者
有些烧写程序指定.test代码长度,升级后的代码长度可能超出烧写指定范围。  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

42

主题

1357

帖子

1

粉丝