当前位置:文档之家› dsp优化心得

dsp优化心得

iPone 的一句大家都耳熟能详的广告词:“一直被模仿,从未被超越” 。

笔者认为主要是因为他们掌握着核心的算法和机器的优化策略。

因为一般的硬件我们都买的回来,但是能否将该硬件发挥到极致,就会公司之间的差别,因为同样的硬件,如果软件执行的速度不同,那结果就会有很大的差别,所以说:真正的技术是买不来的。

所以,我们进行嵌入式开发的时候,一旦选定了DSP6000系列的芯片,就不能把它当成单片机来用,必须发挥dsp 与众不同,独一无二的性能。

也就是说如何调整c 语言才能够适应这么强悍的硬件就是我们考虑的重点内容,即我们应该按照哪种既定的原则去编写C 代码才能够让dsp 真正作为dsp 在工作,发挥到dsp 的优势。

dsp 的优势在于:速度!所以,dsp 的优化成为一门专业。

所以,我们一定要使自己在dsp 上编写的c 代码高效运行。

因为制约运行速度的因素是硬件和软件。

因为dsp 一旦选定,硬件也就确定了。

所以,我们首先要注意如何提升软件的效率。

对于软件来说,一般情况下有3 个优化等级。

第一:算法上优化。

第二:程序结构上的优化。

第三:汇编级的优化。

我们需要的是研究前两个等级的优化。

所以,在这篇文章中,我们需要研究的重点有两个:dsp 的硬件结构和在dsp 上如何优化c 代码。

dsp 的硬件结构关于dsp 硬件结构的特色有几个:哈佛结构,流水线结构,带宽和运算方式的高效等。

1.1.1 哈佛结构哈佛结构的本质属性是数据存储器(RAM存储数据的存储器)和程序存储器(存储指令)分开。

Cpu可以一边取指令,一边取数据。

这样会极大的提高处理的速度,因为以前是冯诺依曼结构,总线是分时复用的,这样会降低处理的速度。

而且,dsp6000系列是基于VLIW结构的,具体来讲就说CPU可以提取通过程序从程序存储器中一次提取256bit的指令,即CPU可以一次取8条指令放在处理中。

加上和8级流水线的配合,相当于8个传统的CPU一起工作。

1.1.2 流水线结构流水线结构涉及的CPU 单元包括取指令单元(fetch ),分配指令单元(dispatch )和执行指令单元(executive )。

这3 个单元都是和程序存储器相关的,是处理指令的几个单元。

首先,程序总线可以一次从ROM中取到8条32位的指令,通俗点说,就说一次取了8条汇编语句。

然后经过取指令单元(4 个步骤,不详述),分配指令单元(2 个步骤),第一个为分配单元,作用是根据指令之间的相关性将这8 条指令再次分成不同大小的执行包,如果两条指令是没有相关的,就可以同时执行,不分先后顺序。

第二个为译码单元,即翻译成可以被执行单元处理的码。

执行指令单元(根据不同的指令分为5 个步骤)。

但8 条指令通过不是一次这4+2+5个步骤,而是在这9 个步骤中都有指令在同时工作。

例如第1个取值包在第1 个步骤,第2 个取值包就在第2 个步骤,第3 个取值包就在第3 个步骤,等等。

剩下要讨论的就说最关键的部分:执行指令单元。

执行指令单元有两个处理指令的通路,每个通路都有4个运算单元:L,S,D,M。

所以指令运算单元就有8个。

我们所说的8级流水线作业也就是因为执行单元有8 个运算单元。

以上就是软件流水线的硬件结构。

这就是最基础最最本质的硬件结构。

我们之后要做的所有的软件优化工作都将作用在这些硬件结构上。

但对于我们做优化来讲,我们不需要对硬件了解太深,但一定要了解软件流水线工作的硬件是如何工作的。

1.1.3 带宽优势和运算方式优势1.1.3.1 带宽优势DSP6000系列能够极大突破速度瓶颈的一个原因就是带宽和运算方式。

所谓带宽就是cpu 一次可以访问的数据量。

我们前面提过,指令就是操作码,和指令相关的单元就说取指令单元,分配指令单元和执行指令单元。

即这些单元就说和程序存储器ROM丁交道的单元。

我们在上面介绍了执行单元的8 个执行指令的运算单元。

但还有32数据通路,即在一个时钟周期内可以从数据存储器RAM中读取32bit的数据。

还有32个通用寄存器(也可以是16 个寄存器对)用来暂时存储操作数。

这些寄存器就是和数据存储器RAM 可接打交道的硬件结构。

这里所说的带宽就是我们在从RAM中读取数据的时候,要尽量利用数据通路的宽度,即我们在编写c代码的时候,一定要“想办法”使CPU^次可以读取32位数据到通用寄存器里面(c64 一次可以读取64 位数据到通用寄存器对里面),因为我们一般情况下处理的原始数据都不是很大,尤其是在图像处理的时候,我们一般情况下使用的是0-255(灰度图像)或0-1(二值图像)。

即使对这些数据做加减乘除运算,得到的结果也不会超过216。

所以,我们一般情况下可以用LDW从数据存储器RAM中读取两个16位的数据(即用short声明的数据)。

所以,一般情况下,我们用short来声明一个数组,然后用LDW来一次读取32位的数据到寄存器中。

这样,我们就可以尽量利用数据通路的宽度,这就是所谓的带宽优势。

1.1.3.2 运算方式优势所谓的运算就说8 个单元可以进行的操作码。

再说具体些就是:加法,减法,乘法,除法,移位,跳转,读取,存储等等。

一般情况下,我们在发挥DSP600C芯片带宽优势的同时要注意运算上的优势。

例如,我们一次可以读取两个16 位的数据放在一个32 位的通用寄存器里。

然后可以用一个双16*16 运算来处理这两个16 位的数据,即我们可以仅仅通过做一次运算就可以进行两个通用寄存器相乘(每个寄存器中的高16 位和低16 位存放的是独立的16 位数据,即两个通用寄存器中有4 个16 位数据),得到的结果就放在一个寄存器对里面。

(如A0:A1 组成的寄存器对,总长64 位,即第1 个结果放在A0里面,第2个结果放在A1里面)。

由于我们是在c 语言层次来讨论dsp 的优化,所以我们要在发挥带宽优势的时候使用内联函数。

因为我们一旦使用了字存取方式来处理数据,就必然会用到相关的内联函数,这两者是联系起来的。

这就是运算方式上的优势。

我们用数据相加来说明运算方式。

传统计算方式上,我们可以一次提取一个16 位数据(放在一个32 位的通用寄存器里),两次就提取两个16 位数据,然后使用一个加法运算,使两个寄存器相加,结果放在放在一个通用寄存器里。

这样一次运算可以使两个16 位的数相加。

如果我们在传统计算方式上进行优化的话,我们可以这样进行,首先使两个数组进行字对齐。

然后使用一个字读取的内联函数读取数据,这样可以一次读取两个16 位的数据放在一个寄存器里,两次就可以读取4 个16 位的数据,之后再使用一个可以进行双16 位数据加16 位数据的内联函数来处理这两个通用寄存器。

这样的话,我们就可以一次处理两个16 位数据和16 位数据的相加。

下面用汇编指令来说明,MPY酣以一次执行双16bit*16bit 的运算。

The following code:MPY A0, A1, A2MPYH A0, A1, A3may be replaced by:MPY2 A0, A1, A3:A2这就是所谓的运算方式上的优势。

一定要记得:带宽优势和运算方式优势是同时发挥的。

dsp 上如何优化 c 代码在C代码基础上进行优化的主要目的就是提高代码执行的速度。

主要策略就是向dsp6000系列的编译器传递一些优化的信息。

这些优化的信息被优化器理解后,就会使编译器将c 代码编译成这些优化信息对应的高效汇编指令,如使用LDW,MYPY等字读取,双16位*16位指令,字对齐,使指令并行执行等。

我们在实际工程应用中,主要针对的就说循环的优化,因为循环是影响执行指令速度的最关键因素。

我们只要优化了循环,就完成了dsp 的优化。

优化循环最大的效果就是使循环的每次迭代参加到流水线中。

下面我们来讨论优化循环的因素。

1.2.1 最重要的因素:循环次数对于一个循环而言,循环次数是最重要的因素,如果能在保证循环功能的情况下减少循环的话(如将16 位读取,16 位相加改称双16 位读取,双16 位相加),那么将极大的优化代码。

减少循环次数将最大限度的减少该循环的指令。

因为从本质上来说,循环次数越多,跳转的次数越多,这样的话CPI处理这些跳转指令的次数越多,负担肯定越大。

循环的汇编指令必须有下面两个指令(红色标记):LOOP:…循环体SUB BO,1,BOfor (i = 0: i < 40; i++){for (j = 0; j < 16;j++)sum += coefs[|] * inputfi + 15 - j];out[i] = (sum » 15);1.2.2}for (i = 0; i < 40; i+中){sum = coefsfO] * input[i -+ 1 5]; sum += coefs[1 ] * input[i + 14]; sum+= coefs[2] * input[i + 13]; sum += coefs[3] * inputfi + 12]; sum +=coefs[4] * input[i +11]; sum += coefs[5] * inputfi + 10];sum += coefs[6] * inpuifl + 9]:sum += coefs[7] “ input[i + 8];sum += coefs[8] * input[i + 7];sum += coefs[9] * inputfi + 6]; sum += ccets[10] * input[i + 5J; sum +=coefs[11 ] * input[i + 4]; sum += coefs[12] * inputfi + 3];sum += coefs[13] * i nput[i + 2];sum += coefs[14] * inputfi + 1];sum 十二coefs[1 5] * input[i + 0];out[i] = (sum >> 15); }units 00・*.S units2* 1・*.D units2* 2*・*.M units2* 2*・*.X cross paths11・*.T address paths2*2*・*Long read paths11・*Long write paths00・*Logical ops (.LS)11(.L or .S unit)・*Additi on ops (.LSD)01(丄 or .S or .Dunit)・*Bound(.L .S 丄S)2*1・*Bound(.L .S .D .LS LSD)2*2*以上是一个循环的资源分配表。

从上面可以看出:一个循环内D单元和M单元都用了两次,平衡了资源的分配。

相关主题