?? c6000優化經驗.txt
字號:
C6XX優化經驗總結
一、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”文件中
二、雙重循環和多重循環的優化總結
雙重循環多重循環看起來比較復雜,但實際上多重循環優化方法比較簡單
,就在于一個字:“拆”,一旦完成這一步之后,多重循環就成為單層循環,優化
就可以按照普通的單層循環來做了。
多重循環的特點是在優化器優化時只在最內層循環中形成一個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 = (sum >> 15);
}
內層循環循環次數較少,運算量也不大,資源方面只占用了一個乘法器,一個cycl
e只使用一次乘法器,而事實上我們可以在一個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 = (sum >> 15);
}
這樣雖然代碼長度增加了,可變成了單循環,所有的運算都參加到pipeli
ne中來,在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 = 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的優化:
三、16位變為32位操作,使用intrinsic函數,用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、優化方法說明:
C6000編譯器提供的intrinsic 可快速優化C代碼,intrinsic用前下劃線表示同調用
函數一樣可以調用它,即直接內聯為C6000的函數。例如,在上例的源代碼中沒有使
用intrinsics,每一行C代碼需多個指令周期,在改編后的代碼中,每一行代碼僅需
一個指令周期。例如,
“aReg_ll = (Word40)_mpyu(L_var1, L_var2)>>16”中“_mpyu”就是一個intrin
sics函數,它表示兩個無符號數的高16位相乘,結果返回。C6000支持的所有intri
nsics指令及其功能參見《TMS320C6000系列DSP的原理與應用》一書的第265、266頁
,該書還提供了另外的例子。這些內聯函數定義在CCS所在的C6000\CGTOOLS\Inclu
de目錄下的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 * b;
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, b);
sum2 += _mpyh(a, b);
}
return sum1 + sum2;
}
技巧:
在C語言的調試全部通過以后,可以嘗試將盡可能多的語句使用intrinsics函數加以
改編,尤其在循環體內,這種改編可以大幅度減少執行時間。
四、
1、源代碼:
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];
}
}
2、改編后的代碼:
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];
}
3、優化方法說明:
C6000編譯器如果確定兩條指令是不相關的,則安排它們并行執行。 關鍵字const可
以指定一個變量或者一個變量的存儲單元保持不變。這有助于幫助編譯器確定指令
的不相關性。例如上例中,源代碼不能并行執行,而結果改編后的代碼可以并行執
行。
4、技巧:
使用const可以限定目標,確定存在于循環迭代中的存儲器的不相關性。
五、
1、源代碼:
void vecsum(short *sum, short *in1, short *in2, unsigned int N)
{
int i;
for (i = 0; i < N; i++)
sum = in1 + in2;
}
2、改編后的代碼:
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 = _add2(in1 , in2);
sum[i+1] = _add2(in1[i+1], in2[i+1]);
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -