????個人覺得雖然沒必要過度優(yōu)化颠焦,現(xiàn)代編譯器在優(yōu)化方面已經(jīng)做得比較好了通今,但是在有些方面民宿,還是需要程序員主動去做的娇妓。
? ? 不過度優(yōu)化≠不主動優(yōu)化
? ? 深度學(xué)習(xí)跑模型的時候,有時候可能一跑就是幾天活鹰,哪怕只對程序優(yōu)化10%哈恰,效果也是非常可觀的志群。在進(jìn)行優(yōu)化的時候着绷,程序可能因此可讀性和可維護(hù)性都變差,這就需要程序員自己去權(quán)衡了锌云,在速度非常重要的時候荠医,做一些犧牲也是可以接受的。
1.高級設(shè)計桑涎。為遇到的問題選擇適當(dāng)?shù)乃惴ê蛿?shù)據(jù)結(jié)構(gòu)彬向。要特別警覺,避免使用那些會漸進(jìn)產(chǎn)生糟糕性能的算法和編碼技術(shù)石洗。
2.基本編碼原則幢泼。避免限制優(yōu)化的因素紧显,這樣編譯器就能產(chǎn)生高效的代碼讲衫。
? ??這點是非常重要的,因為其他優(yōu)化編譯器都可以代替你做孵班,但是以下兩點涉兽,編譯器是沒辦法做優(yōu)化的,至于為什么這兩點會導(dǎo)致編譯器沒辦法優(yōu)化篙程,這里不再贅述枷畏。
1)消除連續(xù)的函數(shù)調(diào)用。在可能時虱饿,將計算移到循環(huán)外拥诡。考慮有選擇性的妥協(xié)程序的模塊性已獲得更大的效率氮发。?
? ? 除此之外渴肉,在可能的情況下,使用內(nèi)聯(lián)函數(shù)也是一個很好的選擇爽冕。
? ? 舉個例子:
? ? 在C++11以前,容器成員函數(shù)size()都是O(n)仇祭,如果采用以下寫法,原本O(n)復(fù)雜度的代碼就變成了O(n^2)颈畸。
????for (int i=0;i<str.size();i++)
? ? 正確的做法是盡量將函數(shù)調(diào)用提到循環(huán)外乌奇。
2)消除不必要的存儲器引用没讲。引入臨時變量來保存中間結(jié)果。只有在最后的值計算出來時礁苗,才將結(jié)果放到數(shù)組或全局變量中爬凑。
? ? 這個應(yīng)該很好理解,學(xué)過計算機(jī)組成原理的都知道存儲器速度遠(yuǎn)低于寄存器速度试伙。
3.低級優(yōu)化?
1)展開循環(huán)贰谣,降低開銷,并且使得進(jìn)一步的優(yōu)化成為可能迁霎。?
? ? 現(xiàn)代編譯器一般都會進(jìn)行循環(huán)展開優(yōu)化吱抚,但是碰到嚴(yán)重限制程序速度的代碼,還是可以嘗試用循環(huán)展開改進(jìn)速度考廉。
2)通過使用例如累積變量和重新結(jié)合等技術(shù)秘豹,找到方法提高指令級并行。?
????①累計變量
????例如a1*a2*a3*a4
? ? 可以改成求(a1*a2)*(a3*a4)
? ? 如果用分治法昌粤,可以把原本O(n)復(fù)雜度減少到O(logn)
????②重新結(jié)合
? ? 那么重新結(jié)合是意思呢既绕,看以下代碼:
????int????a=0,b=1,c=3;
? ? 第一種寫法:????a=a+b+c;
? ? 第二種寫法:? ? a=a+(b+c);
? ? 乍一看這兩種寫法沒什么區(qū)別,但是實際上兩種代碼的關(guān)鍵路徑是不一樣的涮坐。在匯編代碼中凄贩,關(guān)鍵路徑限制代碼執(zhí)行代碼執(zhí)行速度。
? ? 第一種寫法袱讹,應(yīng)該先將a+b保存在寄存器c中疲扎,即存在寄存器寫后讀相關(guān),指令不能并行執(zhí)行捷雕。但是第二種寫法椒丧,b+c保存在不同寄存器中,兩條乘法指令互相不影響救巷。
? ? 以前學(xué)計算機(jī)組成原理的時候壶熏,這種沖突可以通過記分牌算法和Tomasulo算法解決,但是讓硬件去優(yōu)化浦译,還是會增加開銷棒假。我用clang試了一下,編譯器也是會做此類優(yōu)化的精盅。所以一般情況下帽哑,這類優(yōu)化并沒有太大必要。
3)使用功能的風(fēng)格重寫條件操作渤弛,使得編譯采用條件數(shù)據(jù)傳送祝拯。
? ? C++中在可能的情況下,用三目運算符 ?:代替條件語句佳头。(條件傳送指令更快)