C6XX優化經驗總結
1、c6x的編譯的經常使用選項
(一)c6x的編譯程序爲「cl6x.exe」使用的方法
Cl6x [options] [filenames]
Cl6x: 編譯程序
Options: 編譯選項
Filenames: C或彙編源文件
說明:
編譯選項是一個字母或者兩個字母,對大小寫不敏感。
編譯選項的前面須要有一個「-」符號。
一個字母的選項能夠合併在一塊兒。好比「-sgq」與「-s -g -q」相同。
兩個字母的選項若是第一個字母相同也能夠合併在一塊兒。好比「-mgt」與「-mg -mt」相同。
(二)有關優化的選項
-mt:表示在程序中沒有使用alaising技術,這使得編譯器能夠進行比較好的優化。
-o3:對文件級別進行最強的優化,通常在編譯時應該使用這個選項。可是在個別狀況下使用這個選項優化程序可能會出現
錯誤(-o2有相同現象,-o0和-o1不會出現錯誤)。多是在優化循環,組織流水線的時候發生錯誤。若是有這種現象出現能夠同時
使用-g選項,程序優化就不會出現錯誤,可是優化效果會降低。另外能夠調整程序的表達方式,可能會避免編譯器發生錯誤。
-pm:在程序級別進行優化。能夠將因此文件聯合在一塊兒進行優化,主要有去掉沒有被調用的函數、老是常數的變量以及沒有使用的
函數返回值。建議由程序員本身進行這種優化工做。使用這個選項在win98下編譯可能會出現找不到編譯程序的狀況。
-ms0:不使用冗餘循環進行優化,減少程序的大小。通常狀況下這個選項對程序大小的優化做用不明顯。
-mh[n]:去掉流水線的epilog,減少程序的大小。這個選項的做用比較明顯。可是有可能出現讀取地址超出有效範圍的問題,
因此要在數據段的開始和結尾處增長一些pading,或者在分配內存時保證數組的前面和後面一段範圍內都是有效的地址。
可選的參數n給出這種pading的長度字節數。
(三)保留編譯和優化信息的選項
-k:保留優化後生成彙編語言文件。
-s:彙編語言文件中加入優化信息,若是沒有則加入C語言源程序做爲註釋。
-mw:在彙編語言文件加入軟件流水線信息。
(四)有關調試和剖析的選項
-g:容許符號調試,在「out」文件中包含符號信息和行號信息,能夠在c語言級別進行調試和剖析。使用聯合使用-g、-mt和-o3能夠保
證可以進行符號調試的狀況下最大限度的優化。
-mg:容許profile優化後的程序。 在「out」文件中包含符號信息和不多的行號信息。容許在c語言的函數基本進行剖析。
若是聯合使用這兩個選項,-g選項可能被忽略,結果與只用-mg相同。
(五)其它類型
-mln:生成大內存模式的程序。
-ml0:缺省狀況下將集合變量(數組和結構)做爲far型。
-ml1:缺省狀況下將所有函數做爲far型
-ml2: 等於-ml0加-ml1
-ml3: 缺省狀況下將所有數據和函數做爲far型
(六)建議使用的編譯方式
Cl6x -gk -mt -o3 -mw -ss 「filename」
方式1用於程序的調試,這種方式具備比較強的優化能力,而且支持符號調試。在編譯的過程當中不會發生錯誤。
因爲生成的「out」文件中包含了符號信息和行號信息,因此比較大。
Cl6x -k -mgt -o3 -mw -ss 「filename」
方式2用於程序的剖析(profile),這種方式的優化能力幾乎最強(絕大多數狀況下與方式3相同),
而且支持對程序進行profile。文件中只包含了符號信息和不多的行號信息,因此「out」文件比較小。
Cl6x -k -mt -o3 -mw -ss 「filename」
方式3用於最終的發行版本程序,能夠對程序進行最強的優化,而且去掉了所有的符號和行號信息,因此「out」文件比較小。
由多個文件組成的程序應該編寫makefile,將編譯參數放在該文件中,並在其中說明使用的編譯器的版本號。
(七)鏈接參數
-heap:指定堆的大小
-stack: 指定棧的大小
鏈接的各類選項應該統一放在「cmd」文件中
2、雙重循環和多重循環的優化總結
雙重循環多重循環看起來比較複雜,但實際上多重循環優化方法比較簡單,就在於一個字:「拆」,一旦完成這一步以後,
多重循環就成爲單層循環,優化就能夠按照普通的單層循環來作了。
多重循環的特色是在優化器優化時只在最內層循環中造成一個pipeline,這樣循環語句就不能充分利用C6的軟件流水線,
並且對於內部循環的次數較少的狀況,消耗在prolog和eplog上的cycle數也是不可忽視的。
針對這種情況能夠考慮將多重循環拆開造成一個單層循環,能夠拆外層循環也能夠拆內層循環,
通常視具體狀況而定。這樣就能夠充分利用優化器構成的Pipeline。以下例:
void fir2(const short input[], const short coefs[], short out[])
{
int i, j;
int sum = 0;
for (i = 0; i < 40; i++)
{
for (j = 0; j < 16; j++)
sum += coefs[j] * input[i + 15 - j];
out[i] = (sum >> 15);
}
內層循環循環次數較少,運算量也不大,資源方面只佔用了一個乘法器,一個cycle只使用一次乘法器,
而事實上咱們能夠在一個cycle內使用兩個乘法器,因此還能夠充分利用另外的一個乘法器。所以考慮將內層循環拆開來執行,以下:
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);
}
這樣雖然代碼長度增長了,可變成了單循環,全部的運算都參加到pipeline中來,在Piped loop kernal
中產生每個cycle內都使用了兩個乘法器,充分利用了DSP內部的資源,提升了運行效率。又以下例:
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));
}
在這個多層循環中一共有三層循環,而最內層的循環的運算量很小,只有一次乘累加操做,
而咱們知道C6中一個packet中能夠作兩個乘累加運算,因此爲了增長內部循環的運算,減小外部循環的層數,
咱們能夠將第一層循環的操做拆開,其負責的運算加入到內部循環中,也就是在內層循環中一次作四次的乘累加運算,
這樣將屢次操做造成pipeline,提升了運行效率,優化後的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的優化:
3、16位變爲32位操做,使用intrinsic函數,用const等。
一、源代碼:
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);
}
二、改編後的代碼:
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));
}
三、優化方法說明:
C6000編譯器提供的intrinsic 可快速優化C代碼,intrinsic用前下劃線表示同調用函數同樣能夠調用它,即直接內聯爲C6000的函數。
例如,在上例的源代碼中沒有使用intrinsics,每一行C代碼需多個指令週期,在改編後的代碼中,每一行代碼僅需一個指令週期。
例如,
「aReg_ll = (Word40)_mpyu(L_var1, L_var2)>>16」中「_mpyu」就是一個intrinsics函數,它表示兩個無符號數的高16位相乘,
結果返回。C6000支持的全部intrinsics指令及其功能參見《TMS320C6000系列DSP的原理與應用》一書的第26五、266頁,
該書還提供了另外的例子。這些內聯函數定義在CCS所在的C6000/CGTOOLS/Include目錄下的C6X.h文件中。
下面這個例子是C6000的「Programmer's Guide」上提取的使用intrinsics優化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語言的調試所有經過之後,能夠嘗試將盡量多的語句使用intrinsics函數加以改編,
尤爲在循環體內,這種改編能夠大幅度減小執行時間。
4、
一、源代碼:
void fir_fxd1(short input[], short coefs[], short out[])
{
int i, j;
for (i = 0; i < 40; i++)
{
for (j = 0; j < 16; j++)
out[i*16+j]= coefs[j] * input[i + 15 - j];
}
}
二、改編後的代碼:
void fir_fxd2(const short input[], const short coefs[], short out[])
{
int i, j;
for (i = 0; i < 40; i++)
{
for (j = 0; j < 16; j++)
out[i*16+j]= coefs[j] * input[i + 15 - j];
}
三、優化方法說明:
C6000編譯器若是肯定兩條指令是不相關的,則安排它們並行執行。 關鍵字const能夠指定一個變量或者一個變量的存儲單元保持不變。
這有助於幫助編譯器肯定指令的不相關性。例如上例中,源代碼不能並行執行,而結果改編後的代碼能夠並行執行。
四、技巧:
使用const能夠限定目標,肯定存在於循環迭代中的存儲器的不相關性。
5、
一、源代碼:
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];
}
二、改編後的代碼:
void vecsum6(int *sum, const int *in1, const int *in2, unsigned int N)
{
int i;
int sz = N >> 2;
_nassert(N >= 20);
for (i = 0; i < sz; i += 2)
{
sum[i] = _add2(in1[i] , in2[i]);
sum[i+1] = _add2(in1[i+1], in2[i+1]);
}
}
三、優化方法說明:
源代碼中,函數變量的定義是 short *sum, short *in1, short *in2, 改編後的代碼函數變量是
int *sum, const int *in1, const int *in2, 整數類型由16位改編成32位,這時使用內聯指令「_add2」一次能夠完成兩組16位整數的
加法, 效率提升一倍。注意這裏還使用了關鍵字const和內聯指令_nassert優化源代碼。
四、技巧:
用內聯指令_add二、_mpyhl、_mpylh完成兩組16位數的加法和乘法,效率比單純16位數的加法和乘法提升一倍。
6、if...else...語句的優化
(一)
一、源代碼:
if (sub (ltpg, LTP_GAIN_THR1) <= 0)
{
adapt = 0;
}
else
{
if (sub (ltpg, LTP_GAIN_THR2) <= 0)
{
adapt = 1;
}
else
{
adapt = 2;
}
}
二、改編後的代碼:
adapt = (ltpg>LTP_GAIN_THR1) + (ltpg>LTP_GAIN_THR2);
(二)
一、源代碼:
if (adapt == 0)
{
if (filt>5443)
{
result = 0;
}
else
{
if (filt < 0)
{
result = 16384;
}
else
{
filt = _sshl (filt, 18)>>16; // Q15
result = _ssub (16384, _smpy(24660, filt)>>16);
}
}
}
else
{
result = 0;
}
二、改編後的代碼:
filt1 = _sshl (filt, 18)>>16;
tmp = _smpy(24660, filt1)>>16;
result = _ssub(16384, tmp * (filt>=0));
result = result * (!((adapt!=0)||(filt>5443)));
(三)
一、源代碼:
static Word16 saturate(Word32 L_var1)
{
Word16 swOut;
if (L_var1 > SW_MAX)
{
swOut = SW_MAX;
giOverflow = 1;
}
else if (L_var1 < SW_MIN)
{
swOut = SW_MIN;
giOverflow = 1;
}
else
swOut = (Word16) L_var1; /* automatic type conversion */
return (swOut);
}
二、改編後的代碼:
static inline Word32 L_shl(Word32 a,Word16 b)
{
return ((Word32)((b) < 0 ? (Word32)(a) >> (-(b)) : _sshl((a),(b)))) ;
}
三、優化方法說明:
若是在循環中出現if...else...語句,因爲if...else...語句中有跳轉指令,而每一個跳轉指令有5個延遲間隙,
所以程序執行時間延長;另外,循環內跳轉也使軟件流水受到阻塞。直接使用邏輯判斷語句能夠去除沒必要要的跳轉。
例如在例1的源代碼最多有兩次跳轉,而改編後不存在跳轉。例2和例3一樣也去掉了跳轉。
四、技巧:
儘量地用邏輯判斷語句替代if...else...語句,減小跳轉語句。
7、
一、源程序
dm = 0x7FFF;
for (j = 0; j < nsiz[m]; j = add(j, 1))
{
if (d[j] <= dm)
{
dm = d[j];
jj = j;
}
}
index[m] = jj;
二、優化後的程序
dm0 = dm1 = 0x7fff;
d0 = (Word16 *)&d[0];
d1 = (Word16 *)&d[1];
# pragma MUST_ITERATE(32,256,64);
for (j = 0; j < Nsiz; j+=2)
{
n0 = *d0;
d0 += 2;
n1 = *d1;
d1 += 2;
if (n0 <= dm0)
{
dm0 = n0;
jj0 = j;
}
if (n1 <= dm1)
{
dm1 = n1;
jj1 = j+1;
}
}
if (dm1 != dm0)
{
index[m] = (dm1 < dm0)? jj1:jj0;
}
else
{
index[m] = (jj1 > jj0)? jj1:jj0;
}
三、優化說明
求數組的最小值程序,優化時爲了提升程序效率在一個循環以內計算N=1,3,5..和n=2,4,6...的最小值,
而後在比較兩者的大小以求得整個數組的最小值。
8、
一、源程序
for (k = 0; k < NB_PULSE; k++)
{
i = codvec[k];
j = sign[i];
index = mult(i, Q15_1_5);
track = sub(i, extract_l(L_shr(L_mult(index, 5), 1)));
if (j > 0)
{
if (i < l_subfr) code[i] = add(code[i], 4096);
codvec[k] += (2 * L_SUBFR);
}
else
{
if (i < l_subfr) code[i] = sub(code[i], 4096);
index = add(index, 16);
}
if (indx[track] < 0)
{
indx[track] = index;
}
else
{
if (((index ^ indx[track]) & 16) == 0)
{
if (sub(indx[track], index) <= 0)
{
indx[track] = shl((indx[track] & 16), 3)
+ shr(extract_l(L_mult((indx[track] & 15), NB_POS)), 1) + (index & 15);
}
else
{
indx[track] = shl((index & 16), 3)
+ shr(extract_l(L_mult((index & 15), NB_POS)), 1) + (indx[track] & 15);
}
}
else
{
if (sub((indx[track] & 15), (index & 15)) <= 0)
{
indx[track] = shl((index & 16), 3)
+ shr(extract_l(L_mult((index & 15), NB_POS)), 1) + (indx[track] & 15);
}
else
{
indx[track] = shl((indx[track] & 16), 3)
+ shr(extract_l(L_mult((indx[track] & 15), NB_POS)), 1) + (index & 15);
}
}
}
}
二、優化後的程序
for (k = 0; k < 8; k++)
{
i = codvec[k];
j = sign[i];
index = _smpy(i, 6554)>>16;
track = i - index*5;
con = (j > 0);
codvec[k] = codvec[k] + 110*con;
index = index + (!con)*16;
conn = (i < l_subfr);
cono = (j > 0)? 1:-1;
code[i] = code[i] + 4096*conn*cono;
n0 = index;
t0 = indx[track];
n1 = n0&16;
t1 = t0&16;
n2 = n0&15;
t2 = t0&15;
tmp0 = (_sshl(n1,19)>>16) + n2*NB_POS + t2;
tmp1 = (_sshl(t1,19)>>16) + t2*NB_POS + n2;
conp = (((n1 == t1)&&(t0 > n0))||((n1 != t1)&&(t2 <= n2)));
tmp = conp*tmp0 + (!conp)*tmp1;
if (t0 < 0)
indx[track] = n0;
else
indx[track] = tmp;
}
三、優化說明
源程序中在循環中含有許多的if結構,在優化時對if結構首先進行化簡,
再將化簡後的if結構用條件運算表達式進行改寫,最後使循環能夠Pipeline。
9、
一、源程序
for (i = 0; i < n; i++)
{
max = -32767;
for (j = 0; j < n; j++)
{
if (sub (tmp2[j], max) >= 0)
{
max = tmp2[j];
ix = j;
}
}
tmp2[ix] = -32768;
tmp[i] = ix;
}
二、優化後的程序
if (n0>n1) {temp=n0;n0=n1;n1=temp;}
if (n1>n2) {temp=n1;n1=n2;n2=temp;}
if (n2>n3) {temp=n2;n2=n3;n3=temp;}
if (n3>n4) {temp=n3;n3=n4;n4=temp;}
if (n0>n1) {temp=n0;n0=n1;n1=temp;}
if (n1>n2) {temp=n1;n1=n2;n2=temp;}
if (n2>n3) {temp=n2;n2=n3;n3=temp;}
if (n0>n1) {temp=n0;n0=n1;n1=temp;}
if (n1>n2) {return n1;}
三、優化說明
源程序也爲一個求中值的問題,因爲已知循環次數固定爲5,所以將循環展開使用if語句直接求取中值。
10、
一、源程序
static Word16 Bin2int (Word16 no_of_bits, Word16 *bitstream)
{
Word16 value, i, bit;
value = 0;
for (i = 0; i < no_of_bits; i++)
{
value = shl (value, 1);
bit = *bitstream++;
if (sub (bit, BIT_1) == 0)
value = add (value, 1);
}
return (value);
}
for (i = 0; i < prmno[mode]; i++)
{
prm[i] = Bin2int (bitno[mode][i], bits);
bits += bitno[mode][i];
}
二、優化後的程序
value = 0;
bitsp = bits;
bitnop= &bitno[mode][0];
j = *bitnop++;
j1 = *bitnop++;
j2 = *bitnop++;
j3 = *bitnop++;
j4 = *bitnop++;
_nassert(loop[mode]>=35);
for (i = 0; i < loop[mode]; i++)
{
value = value*2 + *bitsp++;
j--;
if (j == 0)
{
*prm++ = value;
value = 0;
j = j1;
j1 = j2;
j2 = j3;
j3 = j4;
j4 = *bitnop++;
}
}
三、優化說明
源程序按照數據位流定義取出參數,爲雙重循環結構,優化中採用從新根據位流的bit長度定義循環次數,
化簡爲單重循環,而後優化循環,去除boundary,使pipeline的數目最小。
11、copy程序的優化
一、源代碼:
Word16 i;
for (i = 0; i < L; i++)
{
y[i] = x[i];
}
二、改編代碼:
(1)要求數組長度能被2整除
Word32 i;
Word32 temp;
int *p1 = (int *)&x[0];
int *q1 = (int *)&y[0];
for (i = 0; i < L/2; i++)
{
temp = *p1++;
*q1++ = temp;
}
(2)要求數組長度能被4整除
Word32 i;
Word32 temp1, temp2;
Word32 *pin1, *pin2, *pout1, *pout2;
pin1 = (Word32 *)&x[0];
pin2 = (Word32 *)&x[2];
pout1= (Word32 *)&y[0];
pout2= (Word32 *)&y[2];
for (i = 0; i < L/4; i++)
{
temp1 = *pin1;
temp2 = *pin2;
pin1+=2;
pin2+=2;
*pout1= temp1;
*pout2= temp2;
pout1+=2;
pout2+=2;
}
三、優化方法說明:
把一次循環拷貝一個word16的數改成一次循環拷貝2個word16或4個word16的數。
四、技巧:
充分利用c6xx一次讀取32位數的特性,並利用一個指令週期能讀取兩個數據的特色。
12、set_zero程序的優化
一、源代碼:
Word16 i;
for (i = 0; i < L; i++)
{
x[i] = 0;
}
二、改編代碼:
(1)數組長度能被2整除
Word32 i;
int *x1 = (int *)&x[0];
for (i = 0; i < L/2; i++)
{
*x1++ = 0;
}
(2)數組長度能被4整除
Word32 i;
int *x1 = (int *)&x[0];
int *x2 = (int *)&x[2];
for (i = 0; i < L/4; i++)
{
*x1 = 0;
*x2 = 0;
x1++;
x2++;
x1++;
x2++;
}
三、優化方法說明:
把一次循環爲一個word16的數賦值改成一次爲2個或4個word16的數賦值。
四、技巧:
充分利用C6XX一次讀取32位數的特色,並利用一個指令週期能讀取兩個數據的特色。
十3、32bit數與16bit數相乘
一、源代碼:
L_tmp0 = Mac_32_16(L_32, hi1, lo1, lo2);
二、改編代碼:
L_tmp0=_sadd(_sadd(_smpyhl(hl32, lo2),
(_mpyus(hl32, lo2)>>16)<<1), L_32);
三、優化方法說明:
hl32是32bit的數,hi1和lo1是16bit的數,且 hl32 = hi 1<<16 + lo1 << 1 ,即hi1和lo1分別是hl32的高16位數和低16位數。
函數Mac_32_16(L_32, hi1, lo1, lo2)實現
L_32 = L_32 + (hi1*lo2)<<1 + ((lo1*lo2)>>15)<<1
源代碼是把一個32位的數拆成兩個16位的數與一個16位的數相乘,優化後的代碼不拆開32位的數,
直接用32位的數與16位的數相乘。運用這種方法必須保證hl32的最低一位數必須爲0,不然應用指令_clr(hl32, 0, 0)把
最低位清零。
四、技巧:
源代碼中的低16位數lo1是hl32的低16位右移一位獲得的(留出一位符號位)。在與lo2相乘時又右移了15位,
因此在改編代碼中右移16位,而且是以無符號數與lo2相乘。
十4、32bit數與32bit數相乘
一、源代碼:
L_tmp = Mac_32 (L_32, hi1, lo1, hi2, lo2);
二、改編代碼:
L_tmp = _sadd(_sadd(_smpyh(hl1_32, hl2_32),
((_mpyhslu(hl1_32, hl2_32)>>16)<<1)+
((_mpyhslu(hl2_32, hl1_32)>>16)<<1)), L_32);
三、優化方法說明:
兩個32位的數相乘,沒必要分紅四個16位的數相乘,直接用32位相乘。其中:
hl1_32 = hi1<<16 + lo1<<1, hl2_32 = hi2 <<16 + lo2 <<1 。
源代碼實現: L_32 = L_32 + (hi1*hi2)<<1 + ( (hi1*lo2)>>15 + (lo1*hi2)>>15 )<<1
四、技巧:
低16位與高16位相乘時,低16位使用的是無符號數。
十5、16位除法的優化
一、源代碼:
Word16 div_s (Word16 var1, Word16 var2) //實現 var1/var2
{
Word16 var_out = 0;
Word16 iteration;
Word32 L_num = (Word32)var1;
Word32 L_denom = (Word32)var2;
for (iteration = 0; iteration < 15; iteration++)
{
var_out <<= 1;
L_num <<= 1;
if (L_num >= L_denom)
{
L_num = L_sub (L_num, L_denom);
var_out = add (var_out, 1);
}
}
return (var_out);
}
二、改編代碼:
Word16 div_s1 (Word16 var1, Word16 var2)
{
Word32 var1int;
Word32 var2int;
var1int = var1 << 16;
var2int = var2 << 15;
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
return (var1int & 0xffff);
}
三、優化方法說明:
實現16位的除法,要求被除數var1和除數var2都是整數,且var1<=var2。利用C6XX特有的指令subc,實現除法的循環移位相減操做。
四、技巧:
把被除數和除數都轉換成32位數來操做,返回時取低16位數。
十6、C6X優化inline舉例:
一、原程序:
for (i = LO_CHAN; i <= HI_CHAN; i++)
{
norm_shift = norm_l(st->ch_noise[i]);
Ltmp = L_shl(st->ch_noise[i], norm_shift);
norm_shift1 = norm_l(st->ch_enrg[i]);
Ltmp3 = L_shl1(st->ch_enrg[i], norm_shift1 - 1);
Ltmp2 = L_divide(Ltmp3, Ltmp);
Ltmp2 = L_shr(Ltmp2, 27 - 1 + norm_shift1 - norm_shift); // * scaled as 27,4 *
if (Ltmp2 == 0)
Ltmp2 = 1;
Ltmp1 = fnLog10(Ltmp2);
Ltmp3 = L_add(Ltmp1, LOG_OFFSET - 80807124); // * -round(log10(2^4)*2^26 *
Ltmp2 = L_mult(TEN_S5_10, extract_h(Ltmp3));
if (Ltmp2 < 0)
Ltmp2 = 0;
// * 0.1875 scaled as 10,21 *
Ltmp1 = L_add(Ltmp2, CONST_0_1875_S10_21);
// * tmp / 0.375 2.667 scaled as 5,10, Ltmp is scaled 15,16 *
Ltmp = L_mult(extract_h(Ltmp1), CONST_2_667_S5_10);
ch_snr[i] = extract_h(Ltmp);
}
*/
二、優化後程序:
//因循環體太大,拆成兩個循環並把相應的函數內嵌以使程序能pipeline,
//用L_div_tmp[]保存因拆分而產生的中間變量。
for (i = LO_CHAN; i <= HI_CHAN; i++)
{
//norm_shift = norm_l(st->ch_noise[i]);
norm_shift = _norm(st->ch_noise[i]);
Ltmp = _sshl(st->ch_noise[i], norm_shift);
//norm_shift1 = norm_l(st->ch_enrg[i]);
norm_shift1 = _norm(st->ch_enrg[i]);
//Ltmp3 = L_shl1(st->ch_enrg[i], norm_shift1 - 1);
LLtmp1 = st->ch_enrg[i];
LLtmp1 = LLtmp1 << (norm_shift1 + 7);
Ltmp3 = (Word32)(LLtmp1 >> 8);
Ltmp2 = IL_divide(Ltmp3, Ltmp);
//Ltmp2 = L_shr(Ltmp2, 27 - 1 + norm_shift1 - norm_shift);
Ltmp2 = (Ltmp2 >> (27 - 1 + norm_shift1 - norm_shift));
if (Ltmp2 == 0)
Ltmp2 = 1;
L_div_tmp[i] = Ltmp2;
}
for (i = LO_CHAN; i <= HI_CHAN; i++)
{
Ltmp2 = L_div_tmp[i];
Ltmp1 = IfnLog10(Ltmp2);
//Ltmp3 = L_add(Ltmp1, LOG_OFFSET - 80807124);
Ltmp3 = _sadd(Ltmp1, LOG_OFFSET - 80807124);
//Ltmp2 = L_mult(TEN_S5_10, extract_h(Ltmp3));
Ltmp2 = _smpy(TEN_S5_10, (Ltmp3 >> 16));
if (Ltmp2 < 0)
Ltmp2 = 0;
Ltmp1 = _sadd(Ltmp2, CONST_0_1875_S10_21);
//Ltmp = L_mult(extract_h(Ltmp1), CONST_2_667_S5_10);
Ltmp = _smpy((Ltmp1 >> 16), CONST_2_667_S5_10);
//ch_snr[i] = extract_h(Ltmp);
ch_snr[i] = (Ltmp >> 16);
}
三、優化說明
觀察上面這個循環,循環體自己比較大,且含有兩個函數L_divide()和
fnLog10(),而C62內部只有32個寄存器,且有些寄存器是系統用的,如B1四、B15這樣循環體太大將會致使寄存器不夠分配,
從而致使系統編譯器沒法實現循環的pipeline。
爲了實現循環的pipeline。咱們須要把循環體進行拆分,拆分時要考慮如下幾點:
(1)、拆分紅幾個循環比較合適?在各個循環能pipeline的前提下,拆開的循環個數越少越好。這就要求儘量讓各個
循環的運算量接近。
(2)考慮在什麼地方把程序拆開比較合適?循環體裏的數據流每每並非單一的,在拆開的斷點處勢必要用中間變量保
存上次的循環運算結果,供之後的循環用。適當的拆開循環體,使所需的中間變量越少越好。
(3)循環體中的函數調用必須定義成內嵌形式,含有函數調用的循環系統是沒法使之pipeline的;各個循環體中的判斷分支
機構不可太多,不然系統也沒法使之pipeline,爲此應近可能把能夠肯定下來的分支肯定下來,並儘量用內嵌指令。
針對上面這個例子,考慮:
(1)爲讓各個循環的運算量大體至關,應把L_divide()和fnLog10()分到兩個循環中去,從循環體大小上考慮,
估計拆成兩個循環比較合適。
(2)考慮在什麼地方把程序拆開比較合適?在
if (Ltmp2 == 0)
Ltmp2 = 1;
後拆開,由於後面用到的數據只有Ltmp2,故只需用一個數組保存每次循環的Ltmp2值便可。
(3)循環體中的兩處函數調用L_divide()和fnLog10()都定義了其內嵌形式,IL_divide()和IfnLog10()。
當把能夠肯定下來的分支做肯定處理,並儘量用內嵌指令後,該循環體中所剩的分支結構已不多,循環體能夠pipeline。
優化前程序用2676 cycle,優化後用400 cycle。優化後兩個子循環的MII分別爲14和6cycle。
內存地址形式: 奔騰,C6000都是32位計算機,字長32,但內存地址都是按字節組織的 一個字4字節(查看內存時候各個字
時候:例如兩個連續字ox1000 ox1004) 寫彙編程序時候,下一個字也須要+4,但寫 C語言時候,int 型,+1就是加4
可是,在Tiger SHARC中,雖然也是32位機,但內存是地址是按字組織的,查看內存時,連續的字地址相差1程序員
//////////////////////////////////////////////////////////////////////////////////本身寫的一段性能很高的代碼///////////////////////////
#include <stdio.h>
#define INTRINSIC編程
short add(short var1,short var2)
{
short var_out;
int L_somme;小程序
L_somme = (int) var1 + var2;
return(var_out);
}
int main()
{
int i,result;
#ifdef INTRINSIC
for(i=0; i<1000;i++)
{
result=_sadd(100000,20);
result>0X00007fff?result=0x7fff:(result<0x8000?result=0x8000:0);
}
#else
for(i=0;i<1000;i++)
add(10,20);
#endif數組
return 0;
}ssh