2012-05-20 257 views
3

如果我有兩個結構:聯成員運營商VS內嵌運營商C++

struct A 
{ 
    float x, y; 
    inline A operator*(A b) 
    { 
     A out; 
     out.x = x * b.x; 
     out.y = y * b.y; 
     return out; 
    } 
} 

具有同等的結構

struct B 
{ 
    float x, y; 
} 

inline B operator*(B a, B b) 
{ 
    B out; 
    out.x = a.x * b.x; 
    out.y = a.y * b.y; 
    return out; 
} 

你會知道的任何理由B的運營商*編譯任何不同,或運行任何慢或快於A的運營商*(即去功能內的實際行動,應該是無關的)?

我的意思是......將宣佈內嵌運營商的一員,相較於未成爲會員,對實際功能的速度,任何一般的效果,無論如何?

我有一個數字,目前按照聯成員操作風格不同結構的......但我想修改它是有效的C代碼,而不是;所以在我這樣做之前,我想知道是否會對性能/編譯有任何改變。

+0

你應該閱讀http://stackoverflow.com/questions/4421706/operator-overloading其中顯示技術比任何一個更快。 –

回答

10

你有它編寫的方式,我期望B::operator*運行速度稍慢。這是因爲「引擎蓋下」實施A::operator*是這樣的:

inline A A::operator*(A* this, A b) 
{ 
    A out; 
    out.x = this->x * b.x; 
    out.y = this->y * b.y; 
    return out; 
} 

所以A將指針傳遞到其左手側參數的功能,同時B必須使該參數的副本之前調用該函數。兩者都必須複製它們的右側參數。

您的代碼會好得多,而且很可能將實現同爲AB,如果你寫使用引用它,並使它const正確:

struct A 
{ 
    float x, y; 
    inline A operator*(const A& b) const 
    { 
     A out; 
     out.x = x * b.x; 
     out.y = y * b.y; 
     return out; 
    } 
} 

struct B 
{ 
    float x, y; 
} 

inline B operator*(const B& a, const B& b) 
{ 
    B out; 
    out.x = a.x * b.x; 
    out.y = a.y * b.y; 
    return out; 
} 

你仍然要返回對象,而不是引用,因爲結果是有效的臨時對象(你沒有返回修改的現有對象)。


附錄

然而,用const傳遞按引用的兩個參數,在B,將它有效地加快使它比A,由於反引用?

首先,當您拼寫出所有代碼時,兩者都涉及相同的解除引用。 (請記住,訪問的this成員意味着指針引用。)

但即便如此,這取決於你的編譯器是如何聰明的。在這種情況下,讓我們說這看你的結構,並決定,因爲這是兩個浮點數不能塞到一個寄存器,所以它會使用指針來訪問它們。因此,取消引用的指針大小寫(這是實現的引用)是最好的。該組件會是這個樣子(這是僞彙編代碼):

// Setup for the function. Usually already done by the inlining. 
r1 <- this 
r2 <- &result 
r3 <- &b 

// Actual function. 
r4 <- r1[0] 
r4 <- r4 * r3[0] 
r2[0] <- r4 
r4 <- r1[4] 
r4 <- r4 * r3[4] 
r2[4] <- r4 

這是假設類RISC架構(比如ARM)。 x86可能使用較少的步驟,但無論如何它都會被指令解碼器擴展到這個詳細程度。問題的關鍵是,它的指針的所有固定偏移指針引用寄存器,這是一樣快,因爲它會得到。優化器可以嘗試變得更聰明並在多個寄存器中實現對象,但是這種優化器難於編寫。 (雖然我有心想準是一個LLVM型的編譯器/優化器能做到這一點很容易優化,如果result僅僅是未保存的臨時對象。)

所以,既然你使用this,你有一個隱含的指針解引用。但是如果物體在棧上呢?沒有幫助;堆棧變量變成堆棧指針的固定偏移量解引用(或者幀指針,如果使用的話)。因此,除非編譯器足夠亮才能將對象分佈到多個寄存器中,否則您最終會在某處取消引用指針。

隨意將-S選項傳遞給gcc以獲取最終代碼的反彙編,以查看您的案例中真正發生了什麼。

+0

謝謝,邁克爾... 我總是忘記使用const或通過引用。 我的錯誤... 但是,對於這兩個參數的const傳引用參數,在B中,由於取消引用,它是否會使其實際上比A更快? – Serge

+1

@Stefan:沒有,因爲它們都涉及非關聯化(一個指針引用'this'指針,另外一個明確的參考參數,但成本是一樣的)。 –

+0

如果編譯器可以證明通過引用/參數傳遞是僅加載參數,則它可以優化指針/引用以傳遞值。 – dirkgently

3

你真的應該把inline -ing留給編譯器。在類定義中所定義(如與A的情況下)

也就是說,功能inline默認。 A::operator *inline說明符是無用的。

更有趣的情況是,當您在類定義之外具有成員函數定義時。在這裏,如果你想向編譯器提供一個提示(它可能會忽略),這是經常使用的,並且應該在調用者內部編譯指令,所以需要內聯。

閱讀C++ FAQ 9

+0

謝謝,dirk! 總是傾斜新的東西... – Serge

+0

還要記住,編譯器(瘋狂地)被允許忽略'inline',當它感覺像。所以你也可能希望將'inline'非成員函數聲明爲'static',以防止它對你這樣做,所以你在鏈接時不會得到多重定義的符號錯誤。 (我「瘋狂地說」,因爲這創造了一種情況,我已經打到'#define'可以強制內聯,而inline'函數不能這樣做,並且原來使用'inline'的代碼實際上更大,並且需要更多堆棧。 ) –

+0

請注意,我說「聲明'inline' ** nonmember **函數爲'static'」。不幸的是,這個'static'關鍵字在這兩種情況下有着截然不同的含義。我的觀點是*完全*關於避免多重定義符號錯誤,因爲編譯器決定不內聯**非成員**函數。另外,我在實踐中遇到了這個問題,因此,並非所有的編譯器都以您描述的方式符合標準。 –

2

這裏是我會寫的結構:

struct A 
{ 
    float x, y; 
    A(float ax, float ay) : x(ax), y(ay) { } 
    A operator*(const A& b) const { return b(x * b.x, y * b.y); } 
} 

要回答這個問題,是寫操作的成員函數可以非常輕微快在某些情況下,但不足以使一個明顯代碼中的差異。

一些注意事項:

  1. 再也不用擔心使用inline關鍵字。優化編譯器 會自行決定什麼和不內聯。

  2. 使用初始化構造函數。這樣做是因爲它們改善了代碼 的可讀性。睡得更好,知道他們可以帶來小小的性能優勢。

  3. 儘可能經常通過const引用傳遞結構。

  4. 專注於編寫具有良好風格的代碼不快。大多數代碼是 足夠快,如果不是,可能是因爲在算法或IO的處理方面骨幹。

+1

你能解釋一下什麼情況會導致潛在的(微小的)速度增加? 如果沒有,沒關係。 – Serge

+0

某些編譯器在某些條件下會傾向於通過寄存器BUT傳遞「this」指針,這隻會意味着函數調用附近的某些其他彙編代碼有點慢。所以:不要擔心。 – cdiggins

+0

哦,非常感謝! – Serge