2013-09-21 28 views
4

我一直聽說關鍵字inline不再適用於現代編譯器,而是用於避免多源項目中的多重定義錯誤。C++`inline`關鍵字和編譯器優化

但今天我遇到了一個編譯器服從關鍵字的例子。

沒有inline關鍵字,下面的代碼

#include <iostream> 

using namespace std; 

void func(const int x){ 
    if(x > 3)  
     cout << "HAHA\n"; 
    else 
     cout << "KKK\n"; 
} 

int main(){ 
    func(5); 
} 

與命令g++ -O3 -S a.cpp,生成與func彙編代碼不內聯。

但是,如果我在func的定義前添加了內聯關鍵字,則func將內聯到main

生成的彙編代碼的一部分是

.LC0: 
    .string "HAHA\n" 
.LC1: 
.string "KKK\n" 
.text 
.p2align 4,,15 
.globl _Z4funci 
.type _Z4funci, @function 
_Z4funci: 
.LFB975: 
    .cfi_startproc 
    cmpl $3, %edi 
    jg .L6 
    movl $4, %edx 
    movl $.LC1, %esi 
    movl $_ZSt4cout, %edi 
    jmp _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l 
    .p2align 4,,10 
    .p2align 3 

main: 
.LFB976: 
    .cfi_startproc 
    subq $8, %rsp 
    .cfi_def_cfa_offset 16 
    movl $5, %edi 
    call _Z4funci 
    xorl %eax, %eax 
    addq $8, %rsp 
    .cfi_def_cfa_offset 8 
    ret 
    .cfi_endproc 

我的編譯器是gcc 4.8.1/x86-64的。

我懷疑該函數可以在鏈接過程中內聯,但我不確定會發生,如果是這樣,我怎麼知道?

我的問題是,爲什麼這個代碼片段似乎是矛盾的,以現代方針如 When should I write the keyword 'inline' for a function/method?

+4

你已經表明'inline'可能影響決定是否內聯,而不是以任何方式更好。 – delnan

+3

它與'static'聲明或'-flto'標誌內聯。 – zch

+2

您引用問題的評分最高的答案是錯誤的。忽略它。 –

回答

3

inline關鍵字有幾個效果。其中之一就是向編譯器暗示你想要內聯函數 - 然而,這並不意味着編譯器必須內聯它[在幾個編譯器中有一個擴展,它說:「內聯這個不管怎麼樣,如果有的話可能的「,例如MS的__forceinline和gcc的__attribute__(always_inline)]。

inline關鍵字還允許您在具有相同名稱的函數中使用同一名稱的多個實例,而不會出現「同一函數的多個定義」的錯誤。 [但是函數每次必須是相同的源]。

在這種情況下,我有點驚訝地看到編譯器不內嵌func。但是,將static添加到func也會使其內聯。很明顯,編譯器根據「其他函數也可能使用func」這一事實來決定這一點,所以我們無論如何都需要一個副本,並且內聯它並沒有太多的收穫。事實上,如果你使一個函數成爲靜態的,並且它只被調用一次,即使函數非常大,gcc/g ++幾乎肯定會內聯它

如果你想讓編譯器內聯某些東西,它永遠不會傷害添加inline但是,在很多情況下,編譯器將一個不錯的選擇任何一種方式。例如,如果我改變的代碼如下:。

const char* func(const int x){ 
    if(x > 3)  
     return "HAHA\n"; 
    else 
     return "KKK\n"; 
} 

int main(){ 
    cout << func(5); 
} 

它內聯留下的funcreturn "HAHA\n";部分

編譯器決定內聯還是不內聯的邏輯非常複雜,其中的一部分是「我們獲得了多少,而它佔用了多少代碼空間」 - 這很可能是調用operator<<(ostream& ,const char *)在這種情況下,對於內聯者來說太多了。不幸的是,並不總是很容易理解爲什麼編譯器需要一個特定的決定......

0

你把什麼聽證會是假的,或者應該是。明確的標準 指定意圖inline:告訴編譯器,如果編譯器可以內聯生成此代碼,那麼它應該是 。直到 編譯器能夠比編程者更好地判斷內存何時需要 ,它應該考慮「提示」。也許 有一天,inline將成爲不相關的(像register有 成爲),但我們還遠沒有。

話雖如此,我很驚訝g ++沒有內聯在你的 的情況下。即使 函數沒有標記爲inline,g ++對於內聯通常也相當積極。也許它只是認爲,因爲 函數不在循環中,所以不值得費心。

+2

編譯器通常會*更好地決定何時內聯。這並不意味着沒有人類專家不擅長的情況,但寄存器分配也是如此 - 只要[問邁克帕爾](http://article.gmane.org/gmane.comp.lang .lua.general/75426)。並且總是存在編譯器特定的「從不內聯」和「如果可以以任何方式總是內聯」屬性。 – delnan

+1

你可以向我們展示一個'inline'優化提示*確實會導致編譯器內聯其他內聯內容的情況嗎?我沒有意識到這種情況(正如我在答案和註釋中提到的那樣,'static'通常也會使編譯器內聯,因爲它不是重要的提示,但它可以避免發送代碼對於函數定義來說,* hint *本身在任何情況下都沒有任何區別,我很樂意看看你是否知道這種情況。 – jalf

+0

@delnan這是簡單的錯誤,編譯器可以來關閉_if_它們會根據profiler輸出執行整個程序優化;但這並不是通常的情況,並且g ++和VC++都不會忽略'inline'聲明,內聯函數不會像'register'一樣 –

2

首先,它不是那麼黑或白。 inline關鍵字的唯一絕對的作用是抑制ODR規則並避免多重定義錯誤。除此之外,編譯器當然可以自由地將關鍵字作爲內聯提示,但它可能會或可能不會。 (從我所看到的,在實踐中,編譯器通常會忽略這個優化提示,因爲大多數人不知道多頻繁一次內聯,或者內聯什麼,編譯器可以做得更好)。但它不忽略提示。

其次,爲什麼將呼叫與關鍵字inline內聯,可能還有另一個原因,但並非如此。

如果沒有inline關鍵字,則必須導出函數定義,因爲另一個TU可能需要鏈接到它。而且由於我們必須導出函數定義,所以代碼已經存在,並且調用內聯意味着您實際上已經重複了函數體。更多的總代碼,更大的可執行文件大小,命中緩存區域。

但是使用inline關鍵字,編譯器不必導出函數定義,因此它可以內聯調用並完全刪除原始定義。然後總代碼大小不會增加(而不是生成函數定義和對它的調用,我們只需將函數體移到調用站點)。

作爲一個實驗,嘗試將該函數標記爲static而不是inline。這也意味着編譯器不需要導出定義,並且很可能導致它確定內聯是值得的。

+0

我不確定你的意思是「編譯器通常會忽略這個優化提示」。從一個或多種QoI來看,它應該只是忽略它,如果它可以比程序員更好的工作,這不是最常見的編譯器的情況下。正如問題指出的那樣,g ++不會忽視它。從我所看到的,也不是VC++。 –

+0

回覆了一句「大多數人不知道多長時間內聯」:我希望,直到他們有一個性能問題沒有一個內聯任何東西。線索來自該計劃的實際表現。 (否則,這是過早的優化。) –

+0

重申你的第三段:根據我的經驗,我爲了性能原因而聲明的大部分函數都在未命名的名稱空間中。你想避免在頭部內聯,正是因爲它破壞了封裝,並引入了編譯器依賴關係。 –