五、DSP优化
其实到这一步,你已经完全可以使用DSP了,接下来,你需要加深熟悉DSP的整个内部结构,主要包含有几个多少位的MAC,有几个多少位的ALU,有几个多少位的数据寄存器等等,还有外部数据总线上连接了哪些外设,内部数据总线是怎么连接的,并且这些数据总线是多少位,这些在Datasheet会有一张很清楚的DSP结构图,还有DSP的整个Memory Map是怎样的,片上有多少Data Memory,有多少Program Memory等等,了解这些其实就是让你知道DSP的运算性能到底可以达到多少,哪些外设会通过外部数据总线传输数据,DSP内部的寄存器是怎么传输数据的,通过这些可以帮助你解决你在开发中遇到的问题,不过最主要的是帮助你对已经编写完成的代码进行优化,我个人认为的优化方法有以下几种:
1、一般编写代码首先是用C,基于C层面优化的方法,我如下举例说几种:
(1)优化循环
for(i = 0;i < max;i ++)
{
for(j = 0; j < max; j ++)
{
float sum = 0.0;
for(k = 0; k < max; k ++)
{
sum += input[i * max + k] * input[j * max + k];
}
cover[i * max + j] = sum / max;
}
}
实例,如input[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16},得出的cover如下:
7 17 27 37
17 43 69 95
27 69 111 153
37 95 153 211
原理:只需要得到左上角或右上角即可,然后半个矩阵赋值给另半个矩阵即可得到整个矩阵。
算法优化后:
for(i = 0; i < max; i ++)
{
for(j = i; j < max; j ++) // 减少一半循环
{
float sum = 0.0;
for(k = 0; k < max; k ++)
{
sum += input[i * max + k] * input[j * max + k];
}
cover[i * max + j] = sum / max;
if(i != j) // 可加可不加,消除中线上的重复赋值
{
cover[j *max + i] = sum / max; // 赋值给另半个矩阵
}
}
}
(2)条件跳转(使用条件跳转会在流水线中浪费更多的周期)
k = k -1;
if(k < -1)
{
k = -1;
}
原理:C语言中的max函数在编译的过程中实际上实现的是DSP中的MAX指令。
优化后:
k = max(k-1, -1);
转换成汇编后:
R0 += -1; // R0 == k;
R1 = -1;
R0 = MAX(R0, R1);
(3)for循环中的条件跳转
for
{
if{...}else{...}
}
原理:减少频繁的条件跳转,当然并不是所有的情况都可以这样做。
优化后:
if
{
for{...}
}
else
{
for{...}
}
(4)使用断言指令来避免条件跳转
if(A)
{
X = exp1;
}
else
{
X = exp2;
}
原理:使用断言指令IF(CC) REG = REG,只会消耗一个周期。
优化后:
X = exp1;
if(!A)
{
X = exp2;
}
(5)除法(取模)操作
一般DSP中不支持除法,除法操作是通过仿真的方式来进行实现,有两种,分为低精度和高精度,但都需要相大多的周期。
除数为2的N次方时可采用右移法。
如为提高性能,可采取查表的方式,这样会损失精度。
隐藏的除法:
for(i = start; i < finish; i += step)
此时编译器会looper = (finish - start) / step 得到次数。
巧妙利用不等式法测:(不过可能会产生溢出,要小心使用)
if(x / y > a / b)
可转换为:
if(x * b > a * y)
(6)数据类型
对于定点DSP而言,对于浮点的操作是用仿真的方式实现的,会消耗很多周期,所以在定点DSP上对于浮点数一般是做定点化的处理,常用的方法我举一个例子:2.5 * a,其实可以转换成80 * a >> 5,不过在定点化时需要注意防溢出。
对于64位数据,也是用仿真的方式实现的,会消耗很多周期。(一般最大仅支持32位数据)
做数字信号处理操作时,如FFT,16bit的操作是比较合适的。
要做控制类,条件跳转时,32bit的操作是比较合适的。
如你的DSP的MAC是16位的,做乘法时,尽量定义成16bit数据。
(7)Memory分配
将运算比较频繁的数据和程序段放入片内Memory,开启cache。
如DSP能对SDRAM的不同4个bank可以同时访问,此时你可以将需要同时运算的数据放入不同的bank
(8)开启仿真软件的编译优化选项
在菜单相应的地方勾上即可,但值得注意时,开启自动编译优化选项后,可能会使执行的结果发生变化,所以需要测试对比一下未开编译优化选项之前的执行结果,一般来说,这个很方便,比较常用。
以上8种是我常用到的优化方法,当然基于C层面算法类的优化还有很多种,这个需要慢慢积累,总结一下,一般来说先对C层面进行结构上的优化(上面的1-6均属于),然后进行Memory分配,开启仿真软件的编译优化选项,将运算频繁的程序段用汇编实现,当然如果性能满足要求,就没必要利用汇编了。
|