2010-09-23 77 views
1

比方說你有一些類中的某些功能被稱爲這樣在一起編譯器優化與參數

myclass::render(int offset_x, int offset_y) 
{ 
    otherClass.render(offset_x, offset_y) 
} 

這種模式將重複一會兒可能通過10+類,所以我的問題是:

現代C++編譯器是否足夠聰明,可以識別程序存儲函數參數的任何地方 - 根據維基百科的說法,它似乎取決於參數大小,但對於2參數函數,處理器寄存器似乎可能 - 不需要被覆蓋有了新的價值?

如果不是我可能需要看看如何實現我自己的方法

+0

問題是什麼? – Arun 2010-09-23 15:34:30

+0

你問是否編譯器將內聯所有這些函數調用? – 2010-09-23 15:36:00

+0

與這樣的小成員,你應該把它們放在類的主體 - 他們將自動內聯屬性,並將內聯(如果優化)。也有其他的事情。與您可以考慮做只頭文件對他們這樣的小功能。項目將加快編制,將是一個有點小 – fazo 2010-09-23 15:50:11

回答

3

我認爲編譯器更有可能進行更大規模的優化。你必須檢查實際的機器代碼生成,但例如以下瑣碎的嘗試:

#include <iostream> 

class B { 
public: 
    void F(int x, int y) { 
     std::cout << x << ", " << y << std::endl; 
    } 
}; 

class A { 
    B b; 

public: 
    void F(int x, int y) { 
     b.F(x, y); 
    } 
}; 

int main() { 
     A a; 
     a.F(32, 64); 
} 

會導致編譯器(cl.exe從2010年VS,空項目,香草「發佈」配置)生成彙編完全內聯在調用樹上;你基本上得到「推40小時,推20小時,打電話std ::運營商< <」。

濫用__declspec(noinline)會導致cl.exe意識到A :: F只是轉發到B :: F,而A :: F的定義只是「調用A :: F」而沒有任何堆棧或寄存器操作所以在這種情況下,它已經執行了你所要求的優化)。但是請注意,我的例子非常有人氣,所以一般來說編譯器沒有能力做到這一點,只是它可以完成。

在您的真實世界中,您必須自己檢查反彙編。特別是,需要考慮'this'參數(cl.exe通常通過ECX寄存器傳遞它) - 如果您對可能影響結果的類成員變量進行了任何操作。

+0

感謝您真正的測試! – Tomas 2010-09-23 15:45:56

+0

在代碼中,'B :: F()'和'A :: F()'內聯(類內)定義。如果這些陳述是在課堂外界定的,那麼這些陳述是否會持 – Arun 2010-09-23 15:49:43

+0

現代編譯器非常擅長選擇內聯的候選人;雖然在類定義之外定義它們確實意味着它們不是以隱式方式聲明爲內聯,但大多數編譯器仍然將它們內聯(cl.exe)。這就是爲什麼我用__declspec(noinline)強行阻止它。 – 2010-09-23 15:53:30

1

我不是專家,但看起來很像是將在未來標準(C++ 0x中)所要解決的perfect forwarding problem使用右值引用。

目前我會說它取決於編譯器,但我想如果函數和參數很簡單,那麼是的函數將作爲一個快捷方式。 如果此函數直接在類定義中被隱式化(然後成爲內聯的候選對象),則它可能是無序的,從而使調用直接調用想要的函數而不是有兩個運行時調用。

+1

完美的轉發是關於模板中'const'的正確性,而不是寄存器的編譯器優化。 – 2010-09-23 15:38:58

0

如果我正確地理解了這個問題,你問「大多數編譯器是否足夠聰明,可以像這樣內聯一個簡單的函數」,並且對這個問題的答案是肯定的。但請注意,作爲函數的一部分的隱式this參數表(因爲您的函數是類的一部分),所以如果調用級別足夠深,它可能不會完全可以內聯。

+0

我的問題不是關於內聯。 – Tomas 2010-09-23 15:44:53

0

內聯的問題是編譯器可能只能對給定的編譯單元執行此操作。鏈接器可能不太適合從一個編譯單元內聯到另一個編譯單元。

但是,考慮到函數的普通性質,並且這兩個函數在相同順序中具有完全相同的參數,函數調用的成本可能僅僅是一個機器指令即viz。一個額外的分支(或跳轉)到真正的實現。甚至不需要將返回地址推入堆棧。

2

是的,它是。編譯器在寄存器分配之前執行數據流分析,跟蹤哪些數據在哪個時間。它會看到arg0位置包含必須在arg0位置,以調用一個函數的值,所以它並不需要移動數據。

1

儘管你的評論,我認爲內聯是有密切關係的討論。我不相信C++編譯器會完成你所要求的工作(在堆棧上重新使用參數),除非它完全使用了這個方法。

的原因是,如果它使一個真正的功能還是叫有把返回地址壓入堆棧,因此在棧上預期的地方不再做以前調用的參數。因此必須再次將參數重新放入堆棧。

但是我真的不擔心。除非你這樣做了一些荒謬的函數調用,並且剖析表明它將大部分時間花費在這些調用上,否則它們的開銷可能極小,你不應該擔心。對於一個小的函數,將它標記爲內聯,並讓編譯器確定它是否可以完全內聯。

+0

那麼我後來的答案是「不,他們不是」 – Tomas 2010-09-23 17:44:12