国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
C64x的軟件優(yōu)化方法 - Polluxa.com -

很早很早很早之前寫的(上研的時候),文章寫的很嫩,但是希望對剛接觸定點優(yōu)化的人有點幫助:
修改了一部分,略了一部分,大部分是從以前的文件粘貼過來的,所以格式比較亂,但是肯定是原創(chuàng),不是copy。

第一章 C6000系列DSP的體系結構簡介
TI的C6000系列DSP內部采用的哈佛結構的體系結構,其數(shù)據(jù)段和代碼段是分開存放的并且獨立編址,減輕了程序在運行時的訪問存儲數(shù)據(jù)的瓶頸。其中C62和C64系列均為定點的DSP處理器,而C67系列為浮點的DSP,本文主要介紹C64系列的優(yōu)化方法。
C6455的功能模塊圖如下:


圖1:c6455的功能方框圖

C64系列DSP有兩個數(shù)據(jù)通道,如下圖:


圖2:C64x的數(shù)據(jù)通道

由上圖可以看出C64x有兩個數(shù)據(jù)通道:Data path A和Data path B。其中每個數(shù)據(jù)通道有32個32-bit寄存器、一個乘法運算單元.M、一個地址產(chǎn)生單元.D、一個邏輯運算單元.L和一個移位運算單元.S。其中,邏輯運算和移位運算并不是單獨在.L和.S單元完成的。它們是由交叉使用的,邏輯運算有可能會用的.S單元,移位運算也有可能用到.L單元。這8個運算單元在一個指令周期內是可以并行工作的,所以說C64x的最大優(yōu)勢就是可以將串行的程序并行處理。兩個數(shù)據(jù)通道共有5個條件寄存器,其中通道A有3個,通道B有2個。在兩個數(shù)據(jù)通道之間還有一個數(shù)據(jù)交叉單元,被稱之為.X,在一個運算單元特別繁忙而另外一個運算單元比較空閑時,較忙的運算單元可以通過.X單元來訪問另外一個數(shù)據(jù)通道的運算單元。當然,在實際的優(yōu)化過程中應該盡量減少.X單元的使用,因為每個指令周期只能做一次數(shù)據(jù)交換,而A和B兩個通道的運算是可以并行的。在優(yōu)化C程序的過程中要充分利用C64x的這個特點,變串行的程序為并行的程序,加快程序的速度。也要充分利用一個.D單元可以存或者取一個32-bit數(shù)的特點,將兩個16-bit合并為一個32-bit來存取,減小.D運算單元的壓力。

第二章 多文件代碼的代碼結構和數(shù)據(jù)結構調整方法
對于一個比較大的程序來說,代碼的結構和模塊化的合理性對代碼的優(yōu)化非常的重要。較好的代碼結構有利于優(yōu)化人員對代碼的理解,有利于對數(shù)據(jù)流的分析和輸入輸出關系的確定。下面就來介紹一種比較好的代碼結構。

模塊的劃分需要對算法有一定程度的理解,所以在整理代碼之前最好能夠閱讀程序文檔和算法文檔,對算法和代碼有一個比較清晰的認識。閱讀代碼時,推薦用Source In Sight和VC Debug結合的方法來閱讀代碼。Source In Sight可以清晰地查看程序的函數(shù)調用關系和數(shù)據(jù)嵌套關系,VC Debug可以方便的分析程序的算法流程。
同時,模塊化也將有助于對算法的理解。下面介紹一種比較流行的文件組織形式。

圖3:文件的組織形式


如圖3所示,最上層的是所有的C源文件,往下一層是各個C文件的頭文件,再由最底層的interface.h文件來將各個C文件和頭文件連接在一起。每一個C文件都首先包含了interface.h,然后包含本身的頭文件。其本身的頭文件中僅僅聲明它上一層的C文件要用到的數(shù)據(jù)類型、表格,不存在交叉的文件包含情況。如果有別的文件要調用本文件的函數(shù)的話,需要在interface.h中聲明。即interface.h中聲明全局的數(shù)據(jù)類型、靜態(tài)表格和函數(shù)。這樣,代碼就呈現(xiàn)出一種清晰的三層結構,其調用關系變的十分的清晰簡單,不會出現(xiàn)重復編譯的情況。而C文件的劃分則是通過模塊來進行劃分,這需要具體的算法具體分析。
數(shù)據(jù)結構的調用關系則也是類似文件的組織形式這樣一個三層的嵌套關系。

圖4:結構體調用關系

以一個感知音頻編碼程序為例,首先底層是一個貫穿于整個程序的結構體,其成員由諸如編碼速率、采樣率,聲道數(shù)之類的編碼參數(shù)和各個模塊的結構體組成;而各個模塊的成員也可以根據(jù)模塊的復雜度再進行嵌套。這樣可以形成一個從最底層開始的按模塊劃分的層層嵌套的數(shù)據(jù)結構組織形式,這種的嵌套方式結構清晰,又避免了各個模塊中重復定義全局變量,也避免了循環(huán)嵌套等復雜的數(shù)據(jù)嵌套關系。
建議程序設計人員在開發(fā)代碼時就按照這樣的一種文件和結構組織形式來開發(fā)代碼,避免了后期封裝帶來的不便,但是如果時間上不允許可以先以功能為主,后期維護再進行程序結構上的調整,畢竟release版本是不存在任何的文件信息和符號信息的。

第三章 軟件的C64x平臺優(yōu)化的基本方法
任何嵌入式軟件在應用之前都要進行平臺的優(yōu)化,軟件的開發(fā)人員一般不會考慮到程序的運行效率和平臺的問題,所以軟件的移植和優(yōu)化就顯得尤為重要。由于現(xiàn)在大部分的平臺都支持標準C,也有相應的GUI的開發(fā)工具,所以軟件的移植則相對比較簡單,但是軟件的平臺優(yōu)化卻需要視平臺的不同而進行。C64x是定點的DSP,其浮點運算都是通過函數(shù)調用的方法來實現(xiàn)的,所以在移植之前首先要進行程序的定點化處理。TI為了方便嵌入式開發(fā)人員的開發(fā)發(fā)行了CCS開發(fā)工具,方便了開發(fā)人員的開發(fā),下一章將簡單介紹一下CCS的編譯環(huán)境配置方法,本章將把重點放在代碼的優(yōu)化上。
C64x軟件優(yōu)化技術的核心思想是串行程序的并行化。


圖5:程序員和編譯器之間的關系

總的來說優(yōu)化可以總結為一下幾條:
1、去除代碼之間的相關性
2、對循環(huán)的處理方法
3、使用對齊的內存操作
4、使用intrinsics
5、使用位寬大的操作
6、軟件流水線的優(yōu)化方法
下面我們來逐一詳細研究這幾個優(yōu)化方法。

一、去除代碼之間的相關性。
這包含了避免內存指針之間的依賴和避免數(shù)據(jù)沖突兩個內容。其中最常用的方法是避免內存指針之間的依賴,這通常也是開始優(yōu)化后的第一步,也是效果最明顯的方法之一。
編譯器在進行程序的優(yōu)化時首先保證程序運行的正確,再盡量的去優(yōu)化程序。例如:
void fun1(int *pSrc, int *pDst)
{
pDst[0]=pSrc[0]+pSrc[1]; //286
pDst[1]=pSrc[2]+pSrc[3]; //287
}

經(jīng)過CCS編譯后的循環(huán)體匯編代碼如下,可以看到一次循環(huán)需要15cycyles。

LDW .D1T1 *A4,A3 :286
LDW .D1T1 *A4(4),A5 :286
NOP 4
ADD .D1 A5,A3,A3 :286
STW .D2T1 A3,*B4 :286
LDW .D1T1 *+A4(8),A5 :287
LDW .D1T1 *+A4(12),A3 :287
RETNOP .S2 B3,3 :288
ADD .D1 A3,A5,A3 :287
STW .D2T1 A3,*+B4(4) :287

其中第三行NOP 4進行了4個指令周期的空操作來等待LDW .D1T1 *A4(4),A5的執(zhí)行完成。為的是確保已經(jīng)將pSrc[0]和pSrc[1]讀取到寄存器A3和A5中。而分析程序我們知道pSrc和pDst是不重疊的,pDst的改變不會影響到pSrc的內容,所以可以把fun1()改寫為以下形式:
void fun2(int* restrict pSrc, int* restrict pDst)
{
pDst[0]=pSrc[0]+pSrc[1]; //286
pDst[1]=pSrc[2]+pSrc[3]; //287
}

從下面的匯編代碼可以看出,一個循環(huán)體之需要9cycles,“||”表示本條指令和上一條指令是并行運行的。

LDW .D1T1 *+A4(8),A6 :287
LDW .D1T1 *+A4(12),A5 :287
LDW .D1T1 *+A4(4),A3 :286
LDW .D1T1 *A4,A4 :286
RETNOP .S2 B3,2 :288
ADD .D1 A5,A6,A5 :287
STW .D2T1 A5,*+B4(4) :287
|| ADD .D1 A3,A4,A3 :286
STW .D2T1 A3,*B4 :286

可以看出,CPU一次性讀進了四個數(shù),然后再進行運算,減少了CPU的等待時間,這樣就縮短了運行周期。
由上面的例子可以看出,如果在一個函數(shù)內部,能夠肯定兩個指針指向的內存之間沒有重疊,則定義指針時用restrict關鍵字修飾。這樣可以在很大程度上提高運算速度。或者使用const關鍵字修飾,將指針聲明為只讀,其效果和restrict效果相同。
C64x在一個時鐘周期內,訪問處于同一個MEM BANK中的兩個數(shù)據(jù)會產(chǎn)生沖突,其中的一個數(shù)據(jù)訪問會被延遲。因此,在設計是應盡量使函數(shù)內部需同時處理的兩個數(shù)據(jù)放在不同的MEM BANK中。具體方法為用DATA_BANK修飾,在定義全局數(shù)組之前用#pragma DATA_BANK(data, bank)聲明,bank取值為:0,2,4,6 (8字節(jié)對齊),data為全劇數(shù)組的數(shù)組名。這種方法僅用于全局數(shù)組的聲明。舉例如下:
int data1[128];
int data2[128];
int data3[128];

void fun1()
{
int k;
for(k=0;k<128;k++){
data2[k]=data1[k]+data3[k];
}
}

上面的函數(shù)一次調用需要1076個指令周期,而用#pragma DATA_BANK聲明以后如下:
#pragma DATA_BANK(data1,0)
#pragma DATA_BANK(data2,2)
#pragma DATA_BANK(data3,4)

int data1[128];
int data2[128];
int data3[128];

void fun1()
{
int k;
for(k=0;k<128;k++){
data2[k]=data1[k]+data3[k];
}
}

優(yōu)化后的程序僅僅需要830個指令周期就可以完成一次函數(shù)調用。

二、對循環(huán)的處理方法
對于循環(huán)的處理,關鍵在于一個字――“拆”。就是將多重循環(huán)盡量拆分為單層循環(huán),因為C64x的編譯器只會對內層循環(huán)進行優(yōu)化。而對于單重循環(huán)程序員只需要給編譯器提供循環(huán)次數(shù)、字節(jié)對齊等信息,建議不要手工展開。手工展開的效果和編譯器展開的效果相同。例如:
for(i=0;i {
int sum=0;
for(j=0;j<8;j++) { sum=sum+val>>j;
}
}

這個for循環(huán)有兩層嵌套,內層的for循環(huán)不僅次數(shù)可以確定,而且循環(huán)體運算簡單,應該將其手工展開:
for(i=0;i>1;
sum=sum+val>>2;
sum=sum+val>>3;
sum=sum+val>>4;
sum=sum+val>>5;
sum=sum+val>>6;
sum=sum+val>>7;
}

這樣一個二重循環(huán)就變成了一個單重循環(huán),且循環(huán)體內沒有打斷流水線的語句,有利于編譯器的優(yōu)化。
由于C64x對循環(huán)的優(yōu)化能力尤其強大,所以在能夠用到循環(huán)得地方盡量用循環(huán)來實現(xiàn),盡量多的給編譯器提供循環(huán)次數(shù)信息,同時也要讓循環(huán)語句的表達式盡量簡單,這樣編譯器可以正確地判斷哪個變量是循環(huán)變量,有利于軟件流水線的優(yōu)化。定義局部變量時,局部變量的聲明周期盡可能小,這樣可以減少對寄存器資源的占用;循環(huán)體內使用的局部變量不要定義在循環(huán)題外面,外部傳入的指針及參數(shù)除外。
循環(huán)是最能體現(xiàn)C64x的軟件流水線的強大功能的,也是最能體現(xiàn)流水線優(yōu)化能力的,所以在軟件流水線的優(yōu)化上也將大量涉及到循環(huán)的優(yōu)化方法,稍后在講解軟件流水線時再著重介紹。

三、使用對齊的內存操作
DM642的硬件結構不僅支持對齊的內存操作,也支持不對齊的內存操作。但是在DM642中對齊的內存操作的效率要高于不對齊的內存操作,其不對齊的內存操作實際上是通過了兩次對其的內存操作來實現(xiàn)的。因此,在程序中應該盡量使用對其的內存操作,在送給編譯器的信息中也盡量得告訴編譯字節(jié)對其信息。優(yōu)化時可以使用_nassert()和其他內存指令通知編譯器,例如:
void fun1(int *restrict pSrc, int *restrict pDst)
{
_nassert(((int)pSrc&7)==0);
_nassert(((int)pDst&7)==0); //兩塊內存都是8字節(jié)對其
int k;
for(k=0;k<40;k++){
pDst[k]=pSrc[k];
}
}

void fun2(int *restrict pSrc, int *restrict pDst)
{
int k;
for(k=o;k<40;k++){ _amemd8(pDst+k)=_amemd8(pSrc+k); //強制使用對齊的內存操作; } }

以上的兩個函數(shù)使用了不同的內存指令,其效果是等價的。但是,需要注意的是在實際的程序優(yōu)化中,在使用內存對齊指令時一定要保證其內存是字節(jié)對齊的,否則程序會出錯。例如假設下面的函數(shù)中數(shù)組a[]是字節(jié)不對齊的,我們想要在屏幕中輸出a[1],比較以下兩個函數(shù):
fun1() { int a1_a0; short a[]={1,2,3,4,5,}; a1_a0=_amem4(&a[1]); printf("a[1]=%d",a1_a0>>16);
}

這個函數(shù)最終的輸出結果為a[1]=1。

fun2()
{
int a1_a0;
short a[]={1,2,3,4,5,};
a1_a0=_mem4(&a[1]);
printf("a[2]=%d",a1_a0>>16);
}

這個函數(shù)最終的輸出結果為a[1]=2。因為a[]在內存中是字節(jié)不對齊的,如果在使用過程中強制使用字節(jié)對其的讀取方式,則讀到寄存器中的將是一個字節(jié)對其的32-bit數(shù),而不是我們想要的a1_a0,程序就會出錯。所以在代碼的優(yōu)化過程中一定要弄清楚此塊內存是不是字節(jié)對齊的,是幾字節(jié)對齊的。

四、intrinsics的使用
DM642支持SIMD(Single Instruction Multiple Date,單指令多數(shù)據(jù))指令,其intrinsics其實就是SIMD匯編指令的C語言組織形式。使用intrinsics會大大提高程序的運算速度。C64x的intrinsics提供了大量對short和char數(shù)據(jù)的快速運算指令,如:_sadd2(), _smpy2()等。所以,如果能夠在程序中使用short或者char數(shù)據(jù)類型的應該首選這兩種數(shù)據(jù)類型,可以方便程序使用intrinsics。如果能夠使用intrinsics的地方盡量去使用它,它可以將C幾個指令周期才能完成的工作在一個指令周期內完成,大大的提高了程序的運行效率。
下面對intrinsics的使用舉一個簡單的例子:
32位飽和加法:sum=_sadd(a,b);
16位飽和加法:sum=_spack2(a+b,0)>>16;
下面列舉一些在優(yōu)化過程中比較常用的幾個intrinsics指令:
_sadd(a,b) 32位飽和加法;
_sadd2(a,b) 兩個16位飽和加法,高16位和高6位相加,低16位和低16位相加;
_ssub(a,b) 32位飽和減法;
_sub2(a,b) 兩個16位減法;
_smpy(a,b) 32位飽和乘法;
_smpy2(a,b) 兩個16位飽和乘法;
_max2(a,b) 找出高16位的最大值和低16位的最大值,再將兩個最大值放到一個32-bit數(shù)中返回,也適合找出兩個short型數(shù)的最大值,如果有較多的if(a<30) a=30之類的語句,可以直接用a=_max2(a,30)來代替,節(jié)省一個條件寄存器;
_min2(a,b) 找出高16位的最小值和低16位的最小值,用法和_max2(a,b)相同;
_norm(a) 找出除符號為以外a中第一個非0位的位置,位置的表示方法是從高位往低位數(shù);
_bitr(a) 比特反轉;
_abs(a) 取a的絕對值;
_spack2(a,b) 將兩個32-bit數(shù)先進行16位飽和,然后把飽和后的a放在一個32-bit數(shù)的高16位,飽和后的b放在低16位上返回;
_rotl(a,b) 將b循環(huán)左移a位;
_subc(a,b) 如果a-b大于0,則返回(a-b)<<1,否則返回(a<<1)+1;
_swap4(a) 將a中的高16位和低16位進行大小頭的轉換;

以上的幾個intrinsics是在優(yōu)化過程中比較常用的intrincics指令,實際的優(yōu)化中還要根據(jù)實際的需要在使用intrinsics,并查閱Ti的相關文檔。從上面列舉的指令可以看出,大部分指令和其匯編指令都是相同的,而且根據(jù)指令是否有2、4等數(shù)字還可以判斷這個指令是對多大的數(shù)據(jù)類型進行操作的。
靈活地使用intrinsics能夠完成許多復雜的操作,例如本節(jié)開始所提到的16位的飽和控制,在C中需要用幾個分支結構來完成。所以靈活使用intrinsics也是優(yōu)化中很重要的方法,尤其是對于計算量比較大且數(shù)據(jù)類型較小的代碼。

五、C64x中對數(shù)據(jù)類型的關注
C64x軟件優(yōu)化中對于數(shù)據(jù)類型的使用有幾個原則。第一,盡可能避免使用int(32bits)和long(40bits)類型;第二,在進行乘法運算時,盡可能使用short*short(1 cycle),而不要使用int*int(5 cycle);第三,循環(huán)計數(shù)變量必須使用int和unsigned int,而不要使用short或者unsigned short,避免使用不必要的符號擴展指令;第四,在使用C6400設備時,要使用-mv6400編譯開關,這樣可以充分利用C64x的硬件資源和特有指令;第五,當處理8bits或者16bits數(shù)據(jù)時,盡可能使用unsigned char和singed short,原因是C64x中intrinsics指令對兩種數(shù)據(jù)有更好的支持。
通過上面五點大致可以總結為盡量使用位寬小的數(shù)據(jù),用位寬大的操作來實現(xiàn)對位寬小的數(shù)據(jù)的多次操作。例如一個復制函數(shù),即將x數(shù)組中所有的數(shù)據(jù)復制到y(tǒng)數(shù)組中:
void fun1()
{
int i;
short x[n];
short y[n];
for(i=0;i {
y[i]=x[i];
}
}

可以優(yōu)化為:
void fun2()
{
int i;
int temp;
int *p1=(int *)&x[0]; //將x[]聲明為int,這樣一次就可以讀入兩個short型數(shù)
int *q1=(int *)&y[0]; //將y[]聲明為int,這樣一次就可以讀入兩個short型數(shù)
for(i=0;i {
temp=*p1++;
*q1++=temp;
}
}

如果n能夠被4整除,也可以一次讀入四個short型數(shù)到一個double型變量中,這樣程序運行效率會更高。通過上面的例子我們也可以推廣到兩個數(shù)組相加,兩個數(shù)組點乘,以及求兩個數(shù)組的最大最小值等操作,也可以結合intrinsics的使用,也可以提高運算的并行度。

六、軟件流水線的優(yōu)化方法
軟件流水線是編譯器針對循環(huán)使用的一種調度技術。使用流水線技術后,可以是多個循環(huán)體并行起來,從而加快循環(huán)的速度。其原理是,將循環(huán)體劃分成多個部分,在流水線的每一個環(huán)節(jié)可同時處理多個循環(huán)體的不同部分,如下圖所示:


圖6:軟件流水線循環(huán)

從圖上我們可以看到,從一個流水線的形成,到流水線的刪除一共經(jīng)歷了三個階段:prolog, kernel, epilog。Prolog是流水線的形成,有4個周期;kernel是流水線的內核,是流水線的核心部分;epilog是流水線的刪除階段,也有四個周期。所以,在優(yōu)化過程中應該盡量的增加流水線的kernel,減小流水線的prolog和epilog階段??梢杂脧娭普归_等方法使流水線充分流起來。
舉個例子:假設循環(huán)次數(shù)為100,在流水線上同時有5個循環(huán)在執(zhí)行;不啟用流水線所需的周期數(shù)=5*100=500周期;啟用流水線所需的周期數(shù)=4+4+96*1=104周期。
前面已經(jīng)提到過,對循環(huán)的流水線優(yōu)化也是C64x軟件優(yōu)化效果最明顯的方法之一,所以對于循環(huán)也有幾點需要注意的:
(1) 盡量不要使用if等分支結構(如必須使用if…else…結構,應將大概律程序塊放在前,避免了后續(xù)的判斷);小型的if…else…也可以用邏輯表達式來展開。例如:
if(A)
c=a;
else
c=b;

可以改寫為
c=((0-A)&a)+((A-1)&b);
C64x有條件寄存器,所以通常沒有這個必要。
(2) 不能有提前終止的指令,如:return, break, continue。在此情況下,編譯器是不會流水化處理的。提前終止可以用多余的計算來保證流水線的暢通,比如說用一個結構體記錄提前終止時的狀態(tài),待循環(huán)完全進行完之后再通過這個結構體恢復到提前終止的狀態(tài)。這種方法雖然引入了大量的多余的計算,但是保證了流水線的暢通。
(3) 不能調用函數(shù),調用函數(shù)會打斷流水線,所以需要調用函數(shù)的地方可以將函數(shù)聲明成內聯(lián)或者寫成一個宏的形式。
(4) 不能使用浮點數(shù)(在定點DSP上,浮點運算都是用函數(shù)實現(xiàn)的),所以在優(yōu)化之前切記應該先進行定點化。
(5) 不能使用除法(在DSP上除法都是用函數(shù)實現(xiàn)的),除法的的替代算法在后面一章LDX的優(yōu)化方法中會介紹。
(6) 循環(huán)體不要太大,建議40cycles以下,強制展開最大120cycles。
(7) 循環(huán)體內指針盡量用restrict聲明,減少CPU的等待時間。
(8) 盡量使內層循環(huán)次數(shù)多而外層循環(huán)次數(shù)少,編譯器只會優(yōu)化最內層循環(huán),所以盡量應該采用單層循環(huán)來進行計算。
(9) 盡量不要在循環(huán)體內部修改循環(huán)計數(shù)變量,否則編譯器將無法判斷循環(huán)次數(shù)。
(10)盡可能給編譯器提供各種有利優(yōu)化的信息(字節(jié)對齊,循環(huán)次數(shù),),尤其重要的是循環(huán)次數(shù)的公約數(shù)。

對于循環(huán)的流水線的優(yōu)化我們可以通過分析.asm文件來進行,.asm文件反饋了編譯的大量信息。一個簡單的循環(huán):
for(i=0;i<40;i++) { a+=i; } 編譯后在asm文件中找到相應的行,得到循環(huán)的軟件流水線信息,asm文件太長,不列舉了

流水線信息首先是循環(huán)的一些基本信息,然后是兩個數(shù)據(jù)通道的是用情況,接下來是寄存器的使用情況,最后是循環(huán)內核。 對于優(yōu)化來說比較重要的信息有:Loop Unroll Multiple,循環(huán)展開倍數(shù),表示了循環(huán)被編譯器幾倍展開,比如2x就表示循環(huán)被2倍展開,4x就表示循環(huán)被四倍展開。Loop Carried Dependency Bound(^),循環(huán)相關性,表示了循環(huán)內核語句之間的相關性,越小越好,在循環(huán)內核中行末有^符號的語句是具有相關性的語句。兩個數(shù)據(jù)通道的使用情況,這里列出了各個運算單元的使用情況,數(shù)字越大,表示使用的越多,一般減小語句之間的相關性應該是優(yōu)化的過程中需要早期解決的問題。*表示了這個運算單元是編譯器優(yōu)化的瓶頸所在,優(yōu)化人員應該設法減小這個單元的負荷;一般情況下應該是A和B兩個通道的使用情況盡量平衡,如果特別不平衡可以將循環(huán)強制展開兩倍。ii=1表示每次循環(huán)迭代占用1個時鐘周期,用了6個功能單元,因為循環(huán)比較簡單,所以這是比較好的優(yōu)化結果。循環(huán)軟件流水線的優(yōu)化目標就是使ii盡量小,使用的功能單元個數(shù)盡量多。 緊接著是寄存器的使用情況,星號表示了在這個時鐘周期內這個寄存器被使用。上例中的寄存器使用情況比較簡單,是因為循環(huán)體比較簡單,實際中寄存器的使用也是一個很重要的問題。我們優(yōu)化的過程中應該尋求寄存器使用飽滿且兩個通道的均衡,最好的結果是A-side和B-side的寄存器在每一個指令周期都被使用,這說明程序已經(jīng)最大限度的挖掘的寄存器的潛能,當然這是不可能。 循環(huán)內核中顯示的是一次循環(huán)的匯編代碼,||表示本條語句和上一條語句可以在一個指令周期內并行進行,然后是助記符,下一列是所使用的功能單元,緊接著是使用的寄存器,最后一列分號之后表示的是編譯之前的行號和相關性標識。優(yōu)化人員可以根據(jù)行號來找到C代碼來進行細節(jié)的優(yōu)化。 我們在通常的優(yōu)化過程中,一般使用5個功能單元已經(jīng)是很好的情況了,除了一些十分簡單的循環(huán),很難達到使用6個以上的功能單元。優(yōu)化時ii值應該盡量的小,具體情況要根據(jù)循環(huán)的復雜程度來看,越復雜的情況ii值越大。 在循環(huán)的優(yōu)化過程中,第一步應該是尋求兩個數(shù)據(jù)通道的平衡,包括功能單元的平衡和寄存器使用的平衡,一般的方法是將循環(huán)強制展開兩次,方法是使用#pragma UNROLL(2),要求循環(huán)次數(shù)是2的倍數(shù)。如果循環(huán)次數(shù)為奇數(shù)可以將循環(huán)拆分為兩個循環(huán)來進行。 接下來需要觀察相關性的問題,如果代碼的相關性很高,就需要減小代碼的相關性,方法有很多,可以將內存中的數(shù)據(jù)全部讀入到寄存器中再進行計算,用restrict關鍵字來修飾指針或者數(shù)組指針等等。 然后觀察運算單元的負載,如果帶有星號的表示這個功能單元是編譯其繼續(xù)優(yōu)化的瓶頸,應該設法減小這個功能單元的壓力,在第一章已經(jīng)介紹了每個功能單元的主要功能,根據(jù)它們對代碼中的計算進行調整即可。注意,一般不可能將星號去掉,只能盡量的減少各個運算單元的壓力,優(yōu)化的目標是各個運算單元平衡,兩條數(shù)據(jù)通道平衡。 最后根據(jù)得到的結果進行代碼的微調。

第四章 一般編譯選項的設置

第五章 LDX算法的C64x平臺優(yōu)化
一~六,略,涉及軟件的具體算法。

七、除法的替代算法
在定點的DSP中,除法通常是通過函數(shù)調用的方法來實現(xiàn)的,因此,不可避免的打斷了流水線。
如果是16位的除法,可以用一下的算法來代替:
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);
}

上面的代碼要求為無符號除法,var1<=var2,得到的結果是Q15定標的結果。

如果是32位除法,本身的記過對精度要求不高時可以用h1/h2的方法實現(xiàn)。
如果除數(shù)較小,小于16位是可以用下面的方法來實現(xiàn):
((h1/l2)<<16)|(l1/l2);
ti提供的除法算法(見TMS320C6000 Integer Division, Aplication Report, SPRA707_, October 2000): unsigned int udiv(unsigned int num, unsigned int den) { int i,shift; if(den>num) return 0;
if(num==0) return 0;
if(den==0) return -1;
shift=_lmdb(1,den)-_lmdb(1,num);
den<<=shift;
for(i=0;i<=shift;i++)
num=_subc(num,den);
return (num<<(32-(shift+1)))>>(32-(shift+1));
}

int sdiv(int num, int den)
{
int i,shift,sign;
sign=(num>>31)^(den>>31);
num=_abs(num);
den=_abs(den);
if(den>num) return 0;
if(num==0) return 0;
if(den==0) return -1;
shift=_lmdb(1,den)-_lmdb(1,num);
den<<=shift;
for(i=0;i<=shift;i++) num=_subc(num,den); num=_extu(num,(32-(shift+1)),(32-(shift+1))); if(sign) return -num; else return num; }

如果得到的結果比較小,且精度要求較高,可以用少量的if分支結構來實現(xiàn)沒有循環(huán)的除法算法。 如:結果絕對值小于32,即2的5次冪。
static int map[6][3]= { {1,0,0,}, //1=1 {0,1,0,}, //2=2 {0,0,1,}, //3=3 {1,0,1,}, //4=1+3 {0,1,1,}, //5=2+3 {1,1,1,}, //6=1+2+3 } int sdiv(int num, int den) { int i,shift,sign; sign=(num>>31)^(den>>31);
num=_abs(num);
den=_abs(den);
if(den>num) return 0;
if(num==0) return 0;
if(den==0) return -1;
shift=_lmdb(1,den)-_lmdb(1,num);
den<<=shift; if(map[shift][0]) { num=_subc(num,den); } if(map[shift][1]) { num=_subc(num,den); num=_subc(num,den); } if(map[shift][2]) { num=_subc(num,den); num=_subc(num,den); num=_subc(num,den); } num=_extu(num,(32-(shift+1)),(32-(shift+1))); if(sign) return -num; else return num; }

第六章 其他的C64x優(yōu)化技巧和嵌入式開發(fā)技巧
1、優(yōu)化過程中如果遇到有多個出口的函數(shù),即有多個return,盡量在邏輯上將提改為僅有一個return;將變量的讀入和計算放在使用之前,減少變量的生存周期和變量對寄存器的占用時間。編譯器在變量聲明的時候是不會給變量分配寄存器的,只有在變量使用的時候才會給變量分配寄存器。如:
{
int a;
//…
a=val;
if(…)
return 0;
//…
ret=a;
return a;
}

上面的代碼中有兩個return,變量a在第一個return之前已經(jīng)賦值,但是在第一個return之后才用到了變量a,應該將a的賦值放在第一個return之后:
{
int a;
//…
if(…)
return 0;
a=val;
//…
ret=a;
return a;
}

這樣會在一定程度上減小代碼需要的指令數(shù)。

2、在C64x中,寄存器是十分寶貴的資源,有著速度快的特點,所以應該充分利用寄存器,在多次使用同一個運算的情況下,應該首先將它計算出來放在寄存器中。對于數(shù)組中的值,如果使用的頻繁可以首先將這個值讀入到寄存器中,避免了大量的讀內存操作。除了充分利用寄存器,另一方面也要節(jié)省寄存器的使用,給編譯器留有優(yōu)化的空間,將局部變量盡量聲明在使用之前。

3、大的局部數(shù)組應該聲明為全局數(shù)組,一方面全局數(shù)組是默認8字節(jié)對齊的,另一方面可以使用#pragma DATA_BANK聲明。而且減小了寄存器的壓力。而在Trimedia平臺中有128個寄存器,一般情況下無需為寄存器不夠用而擔憂。

4、在程序中應該避免使用多維指針,減少指針尋址的次數(shù),多維指針如果可以用比較少維數(shù)的數(shù)組來代替的話應該用比較少維數(shù)的數(shù)組代替,前提是浪費的內存并不多,比如在音頻的編碼算法中經(jīng)常會有單聲道和雙聲道的切換,一個二維指針**q,首先需要第一維分配聲道數(shù)個一維指針,然后每一個聲道再分配需要的內存。這種情況應該改為*q[2],再給需要的指針分配內存(這個方法有沒有效果我深表懷疑,根據(jù)我的分析,我認為無論是二維指針和一維指針數(shù)組,其尋址的次數(shù)應該是相同的,僅僅是少了分配內存的次數(shù))。

5、在需要實現(xiàn)多路編解碼程序中,靜態(tài)表格應當是只讀的,否則如果一路編碼程序改寫了靜態(tài)表格,另外一路讀取到的就不是原始的靜態(tài)表格,容易導致程序的出錯和硬件的死機。

6、在嵌入式開發(fā)過程中,對于內存的操作十分重要,最重要的一點就是分配的內存一定要進行釋放,否則會造成內存泄漏,導致硬件死機。能夠復用的內存一定要復用。在程序的主要編碼循環(huán)中不要進行內存的分配和釋放工作。

7、對于大部分的小函數(shù),聲明稱inline比寫成宏速度快。內聯(lián)函數(shù)必須聲明在其被調用的函數(shù)的文件里,或者在被調用函數(shù)所在文件包含的文件中,否則編譯會出錯。

8、C64x下部分庫函數(shù)和VC下的庫函數(shù)的參數(shù)類型不一樣,如floor在VC下其輸入?yún)?shù)為float型,而在C64x下是double型,如果要實現(xiàn)VC下的功能,應該使用floorf。

9、在CCS中進行profile時,應該在cdb文件中的TSK選項中重新開一個任務,不要再main()函數(shù)中進行剖分,其結構不準確。具體做法一般為,將main()寫成一個空任務,將之前的main()函數(shù)改為新開任務的任務名。
參考文獻

本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
C6000軟件優(yōu)化經(jīng)驗總結zz
C語言關鍵字詳解
go語言系列-從運算符到函數(shù)
少寫點if-else吧,它的效率有多低你知道嗎?
變量的作用域和儲存類型
函數(shù)調用時的??臻g變化
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服