簡體   English   中英

按值傳遞與按引用或按指針傳遞的性能成本?

[英]Performance cost of passing by value vs. by reference or by pointer?

讓我們考慮一個對象foo (它可能是一個int 、一個double 、一個自定義struct 、一個class ,等等)。 我的理解是,經過foo參考函數(或只是一個指針傳遞給foo )帶來更高的性能,因為我們避免做出一個本地副本(這可能是昂貴的,如果foo大)。

但是,從這里的答案看來,實際上可以預期 64 位系統上的指針的大小為 8 個字節,而不管指向的是什么。 在我的系統上, float是 4 個字節。 這是否意味着,如果foo類型為float ,那么它是更有效的只是傳遞foo的值,而不是給它的指針(假定沒有其他限制,這將使使用一個比函數內部的其他更有效)?

這取決於您所說的“成本”是什么意思,以及主機系統(硬件、操作系統)在操作方面的屬性。

如果您的成本度量是內存使用,那么成本的計算是顯而易見的 - 將復制的大小相加。

如果您的衡量標准是執行速度(或“效率”),那么游戲就不同了。 硬件(以及操作系統和編譯器)傾向於通過專用電路(機器寄存器及其使用方式)針對復制特定大小的事物的操作性能進行優化。

例如,機器具有導致“最佳位置”的架構(機器寄存器、內存架構等)是很常見的 - 復制某種大小的變量最“有效”,但復制更大或更小的變量是不那么。 較大的變量復制成本更高,因為可能需要對較小的塊進行多次復制。 較小的也可能花費更多,因為編譯器需要將較小的值復制到較大的變量(或寄存器)中,對其進行操作,然后將值復制回來。

浮點的例子包括一些 cray 超級計算機,它們本機支持雙精度浮點(在 C++ 中也稱為double ),並且在單精度(在 C++ 中也稱為float )上的所有操作都在軟件中模擬。 一些較舊的 32 位 x86 CPU 也在內部使用 32 位整數,並且由於與 32 位之間的轉換,對 16 位整數的操作需要更多的時鍾周期(對於更現代的 32 位或 64 位,情況並非如此)位 x86 處理器,因為它們允許將 16 位整數復制到 32 位寄存器或從 32 位寄存器復制,並對其進行操作,這樣的懲罰較少)。

按值復制一個非常大的結構比創建和復制它的地址效率低,這有點顯而易見。 但是,由於上述因素,“最好按值復制該大小的內容”和“最好傳遞其地址”之間的交叉點不太清楚。

指針和引用往往以類似的方式實現(例如,通過引用傳遞可以以與傳遞指針相同的方式實現),但這並不能保證。

唯一確定的方法是測量它。 並意識到測量值會因系統而異。

有一件事沒有人提到。

有一種稱為 IPA SRA 的 GCC 優化,它自動將“通過引用”替換為“通過值”: https ://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html (-fipa-sra)

這很可能是為標量類型(例如 int、double 等)完成的,它沒有非默認復制語義並且可以放入 cpu 寄存器。

這使得

void(const int &f)

可能一樣快(和空間優化)

void(int f)

因此,啟用此優化后,對小類型使用引用應該與按值傳遞它們一樣快。

另一方面,由於涉及自定義復制語義,因此無法將按值傳遞(例如)std::string 優化為按引用速度。

據我了解,對所有內容使用按引用傳遞永遠不會比手動選擇按值傳遞的內容和按引用傳遞的內容慢。

這對於模板特別有用:

template<class T>
void f(const T&)
{
    // Something
}

總是最優的

您必須測試性能絕對至關重要的任何給定場景,但在嘗試強制編譯器以特定方式生成代碼時要非常小心。

允許編譯器的優化器以它選擇的任何方式重寫您的代碼,只要最終結果可證明是相同的,這可以導致一些非常好的優化。

考慮到按值傳遞浮點數需要制作浮點數的副本,但在正確的條件下,通過引用傳遞浮點數可以允許將原始浮點數存儲在 CPU 浮點寄存器中,並將該寄存器視為“引用”參數到函數。 相比之下,如果傳遞一個副本,編譯器為了保存寄存器的內容,就得找個地方存放副本,甚至更糟的是,它可能因為需要一個寄存器而根本無法使用保留原始值(在遞歸函數中尤其如此!)。

如果您將引用傳遞給可以內聯的函數,這種差異也很重要,其中引用可能會降低內聯的成本,因為編譯器不必保證復制的參數不能修改原始參數。

一種語言越是讓你專注於描述你想要完成的事情而不是你想要如何完成,編譯器就越能夠找到創造性的方法來為你完成艱苦的工作。 特別是在 C++ 中,通常最好不要擔心性能,而是專注於盡可能清楚和簡單地描述你想要的東西。 通過嘗試描述您希望如何完成工作,您將經常阻止編譯器為您優化代碼。

這是否意味着如果 foo 是 float 類型,那么按值傳遞 foo 會更有效?

按值傳遞浮點數可能更有效。 我希望它更有效 - 部分原因是你所說的:浮點數比你描述的系統上的指針小。 但除此之外,在復制指針時,仍然需要對指針進行解引用,以獲取函數內的值。 指針添加的間接性可能會對性能產生重大影響。

效率差異可以忽略不計。 特別是,如果可以內聯函數並啟用優化,則可能不會有任何區別。

您可以通過測量找出在您的情況下按值傳遞浮點數是否有任何性能提升。 您可以使用分析工具來衡量效率。

您可以用參考替換指針,答案仍然同樣適用。

使用引用是否存在某種開銷,即必須取消引用指針的方式?

是的。 引用很可能與指針具有完全相同的性能特征。 如果可以使用引用或指針編寫語義等效的程序,則兩者都可能生成相同的程序集。


如果通過指針傳遞一個小對象比復制它快,那么對於相同大小的對象肯定是這樣,你不同意嗎? 指向指針的指針怎么樣,大約是指針的大小,對吧? (大小完全相同。)哦,但指針也是對象。 因此,如果通過指針傳遞對象(例如指針)比復制對象(指針)快,那么將指向指針的指針傳遞給指向指針的指針......傳遞給指針的速度將比程序快使用更少的指針仍然比不使用指針的指針更快......也許我們在這里找到了無限的效率來源:)

如果您想要優化執行時間以避免隨機訪問,請始終優先考慮通過引用而不是指針。 對於按引用傳遞與按值傳遞,GCC 優化您的代碼,以便不需要更改的小變量將按值傳遞。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM