-
應(yīng)用級別
1.編譯器選項
許多編譯器具有自己的優(yōu)化方案,也是最容易最穩(wěn)定的實現(xiàn)方法
2.調(diào)用高性能庫
如:BLAS FFTW
3.去掉全局變量
4.受限指針
多個指針指向同一個內(nèi)存地址或指向的內(nèi)存地址有重疊,它會阻礙編譯器對程序進行指令重排、表達式移除等優(yōu)化。restrict標識符,指定受限指針
5.條件編譯
-
算法級別
1.索引順序
訪問多維數(shù)據(jù)時的局部性直接與各維數(shù)據(jù)在內(nèi)存中存放的先后順序有關(guān)。如C語言中數(shù)據(jù)是以行為主序存放的,在計算時盡量按行訪問數(shù)據(jù)。
for( int i = 0; i<M; i++){
float ret=0.0f;
for(int j =0 ;j<N;j++){
ret+=a[i][j];
}
r[i]=ret;
}
2.緩存分塊
3.軟件預(yù)取
數(shù)據(jù)被使用前,投機的加載到緩存中
4.查表法
會較少精度,實際項目中將查表法和線性插值結(jié)合減少精度的降低
-
函數(shù)級別
1.函數(shù)調(diào)用參數(shù)
如果函數(shù)的參數(shù)是大結(jié)構(gòu)體或類,應(yīng)當(dāng)通過傳指針或引用減少調(diào)用時復(fù)制和返回時的銷毀開銷
2.內(nèi)聯(lián)小函數(shù)
能夠消除函數(shù)調(diào)用的開銷,并提供更多的指令級并行,表達式移除等優(yōu)化機會。建議少于10行的函數(shù)inline
-
循環(huán)級別
1.循環(huán)展開
展開循環(huán)不但減少了每次的判斷數(shù)量和循環(huán)變量改變的次數(shù),更能增加流水線執(zhí)行的性能
float sum=0.0f;
for(int i=0;i<num;i++){
sum+= a[i];
}
優(yōu)化后:
float sum=0.0f,sum1=0.0f,sum2=0.0f,sum3=0.0f;
for(int i = 0;i< num;i+=4){
sum1 +=a[i];
sum2 +=a[i+1];
sum3 +=a[i+2];
sum +=a[i+3];
}
sum +=sum1+sum2+sum3;
對于二層循環(huán)來說,建議優(yōu)先展開外層循環(huán),但不是一個普適的準則。
需要注意?。?!留意處理末尾的數(shù)據(jù)
2.循環(huán)累積
循環(huán)累積主要和循環(huán)展開同時使用,減少寄存器的使用量的同時保證平行度。
float sum=0.0f,sum1=0.0f,sum2=0.0f;
for(int i = 0;i< num;i+=6){
sum1 +=a[i]+a[i+1];
sum +=a[i+2]+a[i+3];
sum2 +=a[i+4]+a[i+5];
}
sum +=sum1+sum2;
直接展開6次需要6個臨時變量,現(xiàn)在只需要3個。
3.循環(huán)合并
for(int i=0; i<len; i++){{
x1 +=a[i];
}
for(int i=0; i<len; i++){{
x2 *=b[i];
}
合并后:
for(int i=0; i<len; i++){{
x1 +=a[i];
x2 *=b[i];
}
適合于小循環(huán)
4.循環(huán)拆分
相對于循環(huán)合并,就是循環(huán)拆分。
-
語句級別
1.減少內(nèi)存讀寫
2.選用盡量小的數(shù)據(jù)類型
3.結(jié)構(gòu)體對齊
不同硬件平臺和編譯器對結(jié)構(gòu)體對齊的要求不相同,
- 結(jié)構(gòu)體占用總字節(jié)數(shù)盡量是2的冪
- 每個域的開始地址是他大小的整數(shù)倍,比如在32位cpu下,假設(shè)一個整型變量的地址為0x00000004,那它就是自然對齊的
- 編譯器提供了字節(jié)對齊的編譯語句
GCC下則在每個結(jié)構(gòu)加attribute((aligned(4))): - 對于標準數(shù)據(jù)類型,它的地址只要是它的長度的整數(shù)倍就行了,而非標準數(shù)據(jù)類型按下面的原則對齊
1.數(shù)組 :按照基本數(shù)據(jù)類型對齊,第一個對齊了后面的自然也就對齊了。
2.聯(lián)合 :按其包含的長度最大的數(shù)據(jù)類型對齊。
3.結(jié)構(gòu)體: 結(jié)構(gòu)體中每個數(shù)據(jù)類型都要對齊。
struct stu{
char sex;
int length;
char name[10];
};
struct stu my_stu;
由于在x86下,GCC默認按4字節(jié)對齊,它會在sex后面跟name后面分別填充三個和兩個字節(jié)使length和整個結(jié)構(gòu)體對齊。于是我們sizeof(my_stu)會得到長度為20,而不是15.
struct stu{
char sex;
int length;
char name[10];
}__attribute__ ((aligned (4)));
- 表達式移除
去掉重復(fù)的、共同的計算或訪問 - 分支優(yōu)化
1.盡量避免把判斷放到循環(huán)里
2.拆分循環(huán),減少分支
3.合并多個條件
4.查表法移除分支
5.分支順序。如:if( a&&b)若啊計算量大應(yīng)該放后面
6.優(yōu)化交換性能
uchar tmp=a[ji];
a[ji]=a[[jj];
a[jj]=tmp;
可優(yōu)化為:
uchar aji=a[ji];
uchar ajj=a[[jj];
a[ji]=ajj;
a[jj]=aji;
后一段代碼雖然多一個臨時變量,但是讀寫間沒有關(guān)系,并行讀高!
-
指令級別
1.減少數(shù)據(jù)依賴
2.注意處理器多發(fā)射能力
3.優(yōu)化乘除法和模余
整數(shù)運算最多一個周期,而乘法要三個周期,除法十幾個,模余需要幾十或上百,移位運算只要一個周期。
將除法轉(zhuǎn)換成乘法
4.選擇更具體的庫函數(shù)或算法
5.其他:如聲明float時加f后綴,使用const,static