}
內(nèi)層循環(huán)循環(huán)次數(shù)較少,運(yùn)算量也不大,資源方面只占用了一個(gè)乘法器,一個(gè)cycle只使用一次乘法器,而事實(shí)上我們可以在一個(gè)cycle內(nèi)使用兩個(gè)乘法器,所以還可以充分利用另外的一個(gè)乘法器。因此考慮將內(nèi)層循環(huán)拆開來執(zhí)行,如下:
void fir2_u(const short input[], const short coefs[], short out[])
{
int i, j;
int sum;
for (i = 0; i < 40; i++)
{
sum = coefs[0] * input[i + 15];
sum += coefs[1] * input[i + 14];
sum += coefs[2] * input[i + 13];
sum += coefs[3] * input[i + 12];
sum += coefs[4] * input[i + 11];
sum += coefs[5] * input[i + 10];
sum += coefs[6] * input[i + 9];
sum += coefs[7] * input[i + 8];
sum += coefs[8] * input[i + 7];
sum += coefs[9] * input[i + 6];
sum += coefs[10] * input[i + 5];
sum += coefs[11] * input[i + 4];
sum += coefs[12] * input[i + 3];
sum += coefs[13] * input[i + 2];
sum += coefs[14] * input[i + 1];
sum += coefs[15] * input[i + 0];
out[i] = (sum >> 15);
}
這樣雖然代碼長度增加了,可變成了單循環(huán),所有的運(yùn)算都參加到pipeline中來,在Piped loop kernal 中產(chǎn)生每一個(gè)cycle內(nèi)都使用了兩個(gè)乘法器,充分利用了DSP內(nèi)部的資源,提高了運(yùn)行效率。又如下例:
tot = 4;
for (k = 0; k < 4; k++)
{
max = 0;
for (i = k; i < 44; i += STEP)
{
s = 0;
for (j = i; j < 44; j++)
s = L_mac(s, x[j], h[j - i]);
y32[i] = s;
s = L_abs(s);
if (L_sub(s, max) > (Word32) 0)
max = s;
}
tot = L_add(tot, L_shr(max, 1));
}
在這個(gè)多層循環(huán)中一共有三層循環(huán),而最內(nèi)層的循環(huán)的運(yùn)算量很小,只有一次乘累加操作,而我們知道C6中一個(gè)packet中可以做兩個(gè)乘累加運(yùn)算,所以為了增加內(nèi)部循環(huán)的運(yùn)算,減少外部循環(huán)的層數(shù),我們可以將第一層循環(huán)的操作拆開,其負(fù)責(zé)的運(yùn)算加入到內(nèi)部循環(huán)中,也就是在內(nèi)層循環(huán)中一次做四次的乘累加運(yùn)算,這樣將多次操作形成pipeline,提高了運(yùn)行效率,優(yōu)化后的C代碼如下:
tot = 4;
max0=0;
max1=0;
max2=0;
max3=0;
for (i = 0; i <44; i += STEP) //STEP=4, 11 times cirs
{
//code
for (j=0;j<=40-i;j++)
{s0=(Word32)(_sadd(s0,_smpy(hh[j],xx[j+i])));
s1=(Word32)(_sadd(s1,_smpy(hh[j],xx[j+i+1])));
s2=(Word32)(_sadd(s2,_smpy(hh[j],xx[j+i+2])));
s3=(Word32)(_sadd(s3,_smpy(hh[j],xx[j+i+3])));
}
}
//code
CCS的優(yōu)化:
三、16位變?yōu)?2位操作,使用intrinsic函數(shù),用const等。
1、源代碼:
Word32 L_mpy_ll(Word32 L_var1, Word32 L_var2)
{
double aReg;
Word32 lvar;
/* (unsigned)low1 * (unsigned)low1 */
aReg = (double)(0xffff & L_var1) * (double)(0xffff & L_var2) * 2.0;
/* >> 16 */
aReg = (aReg / 65536);
aReg = floor(aReg);
/* (unsigned)low1 * (signed)high2 */
aReg += (double)(0xffff & L_var1) * ((double)L_shr(L_var2,16)) * 2.0;
/* (unsigned)low2 * (signed)high1 */
aReg += (double)(0xffff & L_var2) * ((double)L_shr(L_var1,16)) * 2.0;
/* >> 16 */
aReg = (aReg / 65536);
aReg = floor(aReg);
/* (signed)high1 * (signed)high2 */
aReg += (double)(L_shr(L_var1,16)) * (double)(L_shr(L_var2,16)) * 2.0;
/* saturate result.. */
lvar = L_saturate(aReg);
return(lvar);
}
2、改編后的代碼:
static inline Word32 L_mpy_ll(Word32 L_var1, Word32 L_var2)
{
Word32 aReg_hh;
Word40 aReg,aReg_ll,aReg_lh,aReg_hl;
aReg_ll = (Word40)_mpyu(L_var1, L_var2)>>16;
aReg_lh = (Word40)_mpyluhs(L_var1, L_var2);
aReg_hl = (Word40)_mpyhslu(L_var1, L_var2);
aReg_hh = _smpyh(L_var1, L_var2);
aReg = _lsadd(aReg_ll, _lsadd(aReg_lh, aReg_hl));
aReg = _lsadd(aReg>>15, aReg_hh);
return(_sat(aReg));
}
3、優(yōu)化方法說明:
C6000編譯器提供的intrinsic 可快速優(yōu)化C代碼,intrinsic用前下劃線表示同調(diào)用函數(shù)一樣可以調(diào)用它,即直接內(nèi)聯(lián)為C6000的函數(shù)。例如,在上例的源代碼中沒有使用 intrinsics,每一行C代碼需多個(gè)指令周期,在改編后的代碼中,每一行代碼僅需一個(gè)指令周期。例如,
“aReg_ll = (Word40)_mpyu(L_var1, L_var2)>>16”中“_mpyu”就是一個(gè)intrinsics函數(shù),它表示兩個(gè)無符號(hào)數(shù)的高16位相乘,結(jié)果返回。C6000支持的所有intrinsics指令及其功能參見《TMS320C6000系列DSP的原理與應(yīng)用》一書的第265、266頁,該書還提供了另外的例子。這些內(nèi)聯(lián)函數(shù)定義在CCS所在的C6000\CGTOOLS\Include目錄下的C6X.h文件中。下面這個(gè)例子是C6000的 “Programmer's Guide”上提取的使用intrinsics優(yōu)化C代碼的例子。
源代碼:
int dotprod(const short *a, const short *b, unsigned int N)
{
int i, sum = 0;
for (i = 0; i < N; i++)
sum += a[i] * b[i];
return sum;
}
改編后代碼:
int dotprod(const int *a, const int *b, unsigned int N)
{
int i, sum1 = 0, sum2 = 0;
for (i = 0; i < (N >> 1); i++)
{
sum1 += _mpy (a[i], b[i]);
sum2 += _mpyh(a[i], b[i]);
}
return sum1 + sum2;
}
技巧:
在C語言的調(diào)試全部通過以后,可以嘗試將盡可能多的語句使用intrinsics函數(shù)加以改編,尤其在循環(huán)體內(nèi),這種改編可以大幅度減少執(zhí)行時(shí)間。





