編譯器優化過程具體是做了些什麼,優化後的程序速度能提高多少百分比?


題主問的「編譯器優化」有點指代不明啊,如果「編譯器優化」是指對編譯器自身的優化,那麼Bootstrap(中文譯為自舉?)可能是一個例子。

但我覺得題主問得更可能是編譯器對程序的優化,類似於gcc -o3這樣子的,試著舉幾個簡單的課堂例子,用C語言代碼給出例子幫助理解(實際上應該用IR),拋磚引玉了。高階的還請藍色大大和RednaxelaFX 大大補充了。

編譯器對程序的優化,大體上(我知道的)有這樣幾個原則/思路:

編譯器優化原則之一:能在編譯階段(compile time)做的工作,就不要在程序運行時(runtime)做。

例1: Constant Propagation (常數傳遞?) Constant Folding (中文常數摺疊?)

int freq = 32768;
int secs = 15;
int ticks = freq * secs; //假設後面freq和secs兩個變數沒有別重新賦值

Constant Propagation 優化後:(實際的優化應該在Intermediate Representation里,這裡為了方便理解還是用C語言了)

int ticks = 32768*15;

Constant Folding 優化後:

int ticks = 491520; //編譯器將常數運算結果計算後賦值

這個例子很簡單,相信大家都能看懂。

程序優化前,有3個變數需要3個寄存器,一次乘法運算。

程序優化後,只有1個變數需要一個寄存器,沒有乘法運算。

這個優化看起來很微不足道,但實際上用途很廣。為了程序的可讀性和可維護性,大多數程序員應該還是會選用第一種方式寫3行程序而不是直接甩下一行int ticks = 491520讓後來讀程序的人摸不到頭腦。有了編譯器的優化,程序員既可以寫出易讀的程序又不必擔心性能受影響。尤其是在嵌入式領域,很多低端晶元根本就沒有硬體乘法器,如果程序不做上述優化可能這3行代碼需要幾十個cycle,優化過後一個cycle就搞定,你說性能差了多少? 而付出的代價,只是編譯程序的時間稍微長了那麼一點點,who cares?

類似的優化:Dead code elimination, Common subexpression elimination等

編譯器優化原則之二:對循環優化,優化,再優化!

例2:Loop-invariant code motion (例子來自Wiki)

for (int i = 0; i &< n; i++) { x = y + z; a[i] = 6 * i + x * x; }

很顯然這個循環里,x這個變數被毫無疑義地賦值了n次,這一步驟完全可以在循環之前做。

優化後:

x = y + z;
t1 = x * x;
for (int i = 0; i &< n; i++) { a[i] = 6 * i + t1; }

100個循環,每個循環少執行了一條語句,就相當於是少執行了100條語句啊。要是循環1000次,1000次呢?有的模擬程序都是幾十上百萬次的循環,手賤多寫了一條這樣的語句你說對程序性能影響多大?你說你技術高超不會寫出這樣的程序,但你能保證所有人都不這樣寫嗎?

例3:Loop Unrolling (循環展開)

int array[1024] = ...
for (int i = 0; i &< 1024; i++) { array[i] *= 2; }

優化後:

int array[1024] = ...
for (int i = 0; i &< 1024; i+=4) { array[i] *=2; array[i+1] *=2; array[i+2] *=2; array[i+3] *=2; }

看起來程序優化之後比以前還長了。但實際上,每執行一次循環,都要有一次條件的判斷(i&<1024)。如果不做優化,那麼循環1024次,就要對條件判斷1024次。優化後,只循環256次,那麼判斷的次數就減少到256次。而且這個優化可以將程序並行化,在運算資源充足的時候可以大幅提高運算速度和效率。當然代價是編譯後代碼Size變大。

類似的優化:Software Pipelining, Strength Reduction等等。

之所以對Loop進行著重優化,是因為有統計表明世界上的程序大部分時間都是跑在loop里的。loop裡面優化了一小點,就會對整個程序的性能有很大提升。

最後給一個實際應用的例子:

最近在用某廠DSP晶元做項目,搞DSP的同學都知道DSP上的硬體浮點運算資源非常豐富,但因為程序是從別處扒來的沒有針對DSP進行優化,編譯器又比較蠢,所以DSP並沒有發揮出實際性能。然後我們手動展開了幾個比較核心的loop,這樣運算資源得到充分利用,程序性能提升了3倍。嗯,就做了這一件事兒,性能提升了3倍。懂一點編譯器的知識就是好~

利益相關:不是搞編譯器的,各位輕拍。


總的來說可以分成幾個方向,臨時想的,可能不太全面。

一個是冗餘的刪除,比如死代碼刪除,常量或者複寫傳播,公共子表達式刪除等等

一個是計算強度削弱,包含但不僅限於一些基於邏輯或者算術表達式的優化,例如把乘法轉化成一系列的位移和加減法的組合等

一個是擴大優化器的作用範圍的優化,例如內聯,基本塊合併等等,基本目的在於獲得更大的優化目標

再有就是體系相關的優化,簡單如指令選擇時的一些窺孔性質的指令合併,複雜如寄存器分配,指令調度等np問題的解決。

最後,我想把循環優化單獨歸為一類,這些優化目的錯綜複雜,既有基於強度削弱的循環不變式外提,也有為了獲得更大的循環體以做其他優化的循環展開,還有在此之上來做體系相關優化的向量化軟流水等等

寫完後感覺我這樣分類不太合理…… 先湊合吧


好像自己刷一遍 15-745 Optimizing Compilers for Modern Architectures, Spring 2016 是比在這提問好得多的方式。。

可恥的匿了


昨天剛好碰到一個很有意思的場景,和這位答主探討了下,然後編譯器給出的答案真的驚艷到我了

編譯器能否對如下場景優化,以及如何檢查不同編譯器對此是否做了優化? - 左方園的回答 - 知乎


像Haskell的編譯器,開了優化選項,能把指數級的複雜度優化成多項式…


優秀程序員讓編譯器無事可做:)


o0和o3差別可是相當的大


深入理解Java虛擬機:JVM高級特性與最佳實踐 第2版 高清PDF+源碼_Linux下載_Linux公社-Linux系統門戶網站

第十章


推薦閱讀:

深度學習如何影響運籌學?
優化器優化編譯器,然後優化後的編譯器又重新編譯優化器,一直循環達到最優?
新到一家P2P公司在寫網站推廣方案,由於沒有寫過,希望大家給些補充和意見?謝謝

TAG:優化 | 編譯原理 | 編譯器 |