2013-11-26 61 views
0

這可能是一個簡單的問題,但是這讓我想起了。這是關於下面的兩個功能之間的區別:C++函數中的效率?

T func_one(T obj) {  //for the purpose of this question, 
    return obj + obj; //T is a large object and has an overloaded '+' operator 
} 

T func_two(T obj) { 
    T output = obj + obj; 
    return output; 
} 

func_one(),而不是創建一個對象T,分配一個值,然後返回對象,我只是返回的值本身,而無需創建一個新的對象。如果T是一個很大的對象,那麼func_one()會比func_two()更有效嗎?或者func_one()是否會在返回兩個對象的總和時創建對象T

+0

你不應該像這樣添加和返回大對象。 – Mikhail

+2

任何現代優化編譯器都會爲兩者生成類似的程序集。此外,藉助RVO和移動語義,這可能是完成此操作的最有效方式。要知道的唯一方法是配置文件。 – Chad

回答

0

編譯器會將fund_two優化爲類似於func_one的東西,然後將其優化爲其他內容,長話短說,除非您確實需要擔心這一點,否則您不必擔心這一點,那麼在這種情況下你可以看看asm輸出。

0

簡短的回答:我們無法知道

龍回答:它高度依賴於T是如何工作的,你的編譯器的返回值優化支持。

任何按值返回的函數都可以應用RVO或NRVO優化。這意味着它會將返回值直接構造到調用函數中,從而消除複製構造函數。由於這是按值返回大對象的問題,這將意味着性能上的顯着提高。

func_one和func_two的區別在於func_one返回一個匿名臨時值,一個r值;這意味着可以輕鬆使用RVO。 func_two返回一個命名值,一個l值,所以將使用更難的優化NRVO。然而,func_two是微不足道的,所以它幾乎肯定會應用NRVO,並且這兩個函數將基本相同。

這是假設你有一個現代或甚至半現代編譯器;如果不是,它將取決於你如何實現T.

如果T有移動語義,你的編譯器將能夠移動而不是複製。這應該適用於兩種功能,因爲兩者都有臨時性;但是,由於func_two返回一個命名值,因此可能無法使用移動語義。這取決於編譯器,如果編譯器沒有執行RVO或NRVO,我懷疑它正在執行。

最後,這取決於+ operator和= operator是如何實現的。例如,如果它們被實現爲表達式模板,那麼fun_two仍然需要一個賦值,這會減慢賦值,func_one將返回一個高度優化的臨時值。

總結 在幾乎所有的實際情況下,這些是相同的。在你的編譯器非常奇怪的消失的小窗口中,func_one幾乎普遍更快。

0

現代編譯器可以將帶有額外變量的版本轉換爲沒有的版本(命名爲返回值優化,例如,這是SO的常見問題來源,例如Why isn't the copy-constructor called when returning LOCAL variable)。所以這不是你應該擔心的開銷。

您應該擔心的開銷是函數調用開銷。最多隻需一個週期就能完成一個現代CPU。函數調用需要10到20個週期,具體取決於參數的數量。

我在你的問題中有點不確定你的意思是T(它是一個模板參數嗎?它是一個類嗎?它是一個你不想在你的問題中公開的類型的佔位符?)。但是,您是否有函數調用開銷問題取決於該類型。這取決於你的編譯器是否可以內聯你的函數。

  • 很明顯,如果它被內聯,你很好,沒有函數調用的開銷。
  • 如果T是一個複雜類型,並且代價很大,那麼你也很好。
  • 但是,如果Tint,並且您的函數沒有內聯,那麼您的函數大約有90%的開銷。