串行代碼優(yōu)化(個人總結(jié))

  • 應(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容