2013-08-02 52 views
2

現代編譯器中的優化越來越好,通過使用SIMD指令進行常量摺疊等基本優化。但是,我想知道這種優化應該在多遠以及編譯器如何做出這樣的決定。提前編譯器如何確定優化有多遠?

讓我們來看一個例子:

#include <stdio.h> 

static double naive_sin(double n) { 
    return n - n*n*n/6.0 + n*n*n*n*n/120.0 + n*n*n*n*n*n*n/5040.0; 
} 

int main() { 
    printf("%f\n", naive_sin(1.0)); 

    return 0; 
} 

當與GCC與-O3編譯此,可以觀察到,得到的浮點數是由編譯器計算並存儲在源代碼。進一步優化顯然是不可能的。

現在,讓我們來看看第二個例子:

#include <stdio.h> 

int main() { 
    double start = 0.0; 

    for (int i = 0; i < 100; i++) { 
     start += 1.0; 
    } 

    printf("%f\n", start); 

    return 0; 
} 

考慮到第一個例子的結果,人們可以期望編譯器採用類似的優化和所產生的機器代碼產生恆定100.0。但是,在查看輸出時,事實證明循環仍然存在!

顯然這種優化並不總是可能的。假設你正在編寫一個計算pi的程序,達到100萬個地方。這樣的程序不需要用戶輸入,因此理論上可以通過編譯器將結果硬編碼到機器代碼中。當然,這不是一個好主意,因爲編譯器在內部評估程序時需要更長的時間,而不是僅僅運行較少優化的版本。

但是,在這種情況下,編譯器決定不優化循環的原因是什麼?有沒有優化這種代碼的語言/編譯器,或有什麼阻止這種情況?這可能與不能預測一個項目是否會結束的概念有關嗎?

+4

「提前編譯器如何確定優化有多遠?」 - 它看起來在命令行標誌:)) – 2013-08-02 23:12:03

+1

「無法預測程序是否會結束」是(FYI)稱爲[暫停問題](http://en.wikipedia.org/wiki/) Halting_problem)。請注意,編譯器可以並且展開一些循環,因此編譯器可以在*某些情況下證明循環將終止。 – cdhowie

+4

這是一個非問題。 AOT處理代碼的*圖形*,它實際上不執行*代碼。程序員不會編寫無限嵌套的函數。 –

回答

3

這實際上只是一個啓用優化的問題,以及編譯器中實際可用的優化。一些優化,如函數內聯和常量傳播,基本上普遍可用並且相對容易實現。因此,大多數編譯器會使用大多數優化設置來優化第一個程序。

第二個程序需要循環分析和循環消除來優化,這是非常棘手的事情。一個編譯器可能可能優化第二個程序,但你的編譯器很可能沒有優化這種循環的機制(證明浮點優化的正確性通常比證明整數優化的正確性要複雜得多)。請注意,如果start被聲明爲int,我的GCC版本確實優化了循環。