簡體   English   中英

在 C 中,在函數中使用靜態變量會使其更快嗎?

[英]In C, does using static variables in a function make it faster?

我的函數將被調用數千次。 如果我想讓它更快,將局部函數變量更改為靜態是否有用? 我背后的邏輯是,因為靜態變量在函數調用之間是持久的,它們只在第一次分配,因此,每次后續調用都不會為它們分配內存並且會變得更快,因為內存分配步驟沒有完成。

此外,如果上述情況屬實,那么每次調用函數時使用全局變量而不是參數會更快地將信息傳遞給函數嗎? 我認為參數的空間也在每個函數調用中分配,以允許遞歸(這就是遞歸占用更多內存的原因),但由於我的函數不是遞歸的,如果我的推理是正確的,那么在理論上取下參數會使它更快。

我知道我想做的這些事情是可怕的編程習慣,但是請告訴我這是否明智。 無論如何我都會嘗試,但請給我你的意見。

局部變量的開銷為零。 每次調用函數時,您都已經為參數、返回值等設置了堆棧。添加局部變量意味着您向堆棧指針(在編譯時計算的數字)添加了一個稍大的數字.

此外,由於緩存局部性,局部變量可能更快。

如果您只調用您的函數“數千”次(而不是數百萬或數十億次),那么您應該在運行分析器查看您的算法以尋找優化機會。


回復:緩存局部性(在此處閱讀更多內容):經常訪問的全局變量可能具有時間局部性。 它們也可能在函數執行期間被復制到寄存器中,但在函數返回后將被寫回內存(緩存)(否則它們將無法被其他任何東西訪問;寄存器沒有地址)。

局部變量通常具有時間和空間局部性(它們通過在堆棧上創建而獲得)。 此外,它們可能會直接“分配”到寄存器,而永遠不會寫入內存。

找出答案的最好方法是實際運行分析器。 這可以像使用這兩種方法執行幾次定時測試一樣簡單,然后對結果求平均值並進行比較,或者您可以考慮一個成熟的分析工具,它將自身附加到進程並繪制出內存使用隨時間和執行速度的圖。

不要執行隨機的微代碼調整,因為你有直覺它會更快。 編譯器對事物的實現都略有不同,並且在一個環境中的一個編譯器上正確的內容在另一種配置上可能是錯誤的。

為了解決關於更少參數的評論:“內聯”函數的過程實質上消除了與調用函數相關的開銷。 編譯器可能會自動內聯一個小函數,但您也可以建議內聯一個函數

在另一種語言 C++ 中,即將推出的新標准支持完美轉發和帶有右值引用的完美移動語義,這在某些情況下消除了對臨時變量的需求,從而降低了調用函數的成本。

我懷疑您過早地進行了優化,但是,在您發現真正的瓶頸之前,您不應該如此關注性能。

絕對不是! 唯一的“性能”差異是初始化變量的時間

    int anint = 42;
 vs
    static int anint = 42;

在第一種情況下,每次調用該函數時,整數將設置為 42,在第二種情況下,當程序加載時,將設置為 42。

然而,差異是如此微不足道,以至於幾乎無法察覺。 這是一個常見的誤解,即每次調用都必須為“自動”變量分配存儲空間。 這並不是 C 將堆棧中已經分配的空間用於這些變量。

靜態變量實際上可能會減慢您的速度,因為它無法對靜態變量進行一些激進的優化。 此外,由於本地人位於堆棧的連續區域,因此更容易有效地緩存。

對此沒有一個答案。 它會隨着 CPU、編譯器、編譯器標志、你擁有的局部變量的數量、在你調用函數之前 CPU 所做的事情以及很可能是月相而變化。

考慮兩個極端; 如果您只有一個或幾個局部變量,它/它們可能很容易存儲在寄存器中,而不是完全分配內存位置。 如果寄存器“壓力”足夠低,這可能會在根本不執行任何指令的情況下發生。

在相反的極端情況下,有一些機器(例如 IBM 大型機)根本沒有堆棧。 在這種情況下,我們通常認為的堆棧幀實際上被分配為堆上的鏈表。 正如您可能猜到的那樣,這可能會慢。

在訪問變量時,情況有些相似——訪問機器寄存器的速度可以保證比內存中分配的任何內容都快。 OTOH,訪問堆棧上的變量可能會非常慢——它通常需要諸如索引間接訪問之類的東西,這(尤其是對於較舊的 CPU)往往相當慢。 OTOH,訪問全局(靜態是,即使其名稱不是全局可見的)通常需要形成絕對地址,某些 CPU 也會在某種程度上對其進行懲罰。

底線:即使是分析代碼的建議也可能放錯地方——差異可能很小,以至於分析器也無法可靠地檢測到它,唯一確定的方法是檢查生成的匯編語言(和花費數年時間學習匯編語言不夠好,知道說什么,當你在它的樣子)。 另一方面是,當您處理一個差異時,您甚至無法可靠地衡量,它對實際代碼的速度產生實質性影響的可能性非常小,以至於可能不值得麻煩。

使用靜態變量可能會使函數更快一點。 然而,如果你想讓你的程序成為多線程,這會導致問題。 由於靜態變量在函數調用之間共享,因此在不同線程中同時調用函數將導致未定義的行為。 多線程是您將來可能想要做的真正加快代碼速度的事情。

您提到的大部分內容都稱為微優化。 一般來說,擔心這些事情是一個壞主意 它使您的代碼更難閱讀,也更難維護。 它也極有可能引入錯誤。 在更高級別進行優化,您可能會獲得更多回報。

正如 M2tM 所建議的那樣,運行分析器也是一個好主意。 查看gprof中的一個,它非常易於使用。

看起來靜態與非靜態已經完全涵蓋,但關於全局變量的主題。 通常這些會減慢程序的執行速度而不是加速它。

原因是范圍緊密的變量使編譯器很容易進行大量優化,如果編譯器必須在您的應用程序中查看可能使用全局變量的實例,那么它的優化將不會那么好。

當您引入指針時,情況變得復雜,假設您有以下代碼:

int myFunction()
{
    SomeStruct *A, *B;
    FillOutSomeStruct(B);
    memcpy(A, B, sizeof(A);
    return A.result;
}

編譯器知道指針 A 和 B 永遠不會重疊,因此它可以優化副本。 如果 A 和 B 是全局的,那么它們可能指向重疊或相同的內存,這意味着編譯器必須“安全行事”,這會更慢。 該問題通常稱為“指針別名”,不僅在內存復制中,而且在很多情況下都可能發生。

http://en.wikipedia.org/wiki/Pointer_alias

您始終可以為您的應用程序計時,以真正確定什么是最快的。 這是我的理解:(所有這些都取決於您的處理器架構,順便說一句)

C 函數創建一個堆棧幀,其中放置傳遞的參數和局部變量,以及返回調用者調用函數的位置的返回指針。 這里沒有內存管理分配。 它通常是一個簡單的指針移動,僅此而已。 訪問堆棧外的數據也非常快。 當你處理指針時,懲罰通常會起作用。

至於全局或靜態變量,它們是相同的……從它們將被分配在同一內存區域的角度來看。 訪問這些可能使用與局部變量不同的訪問方法,這取決於編譯器。

您的方案之間的主要區別是內存占用,而不是速度。

使用靜態變量實際上會使您的代碼顯着變慢 靜態變量必須存在於內存的“數據”區域中。 為了使用該變量,該函數必須執行加載指令以從主內存讀取,或執行存儲指令以寫入該變量。 如果該區域不在緩存中,則會丟失許多周期。 存在於堆棧中的局部變量肯定會有一個位於緩存中的地址,甚至可能位於 cpu 寄存器中,根本不會出現在內存中。

我同意其他人關於分析以找出類似內容的評論,但一般來說,函數靜態變量應該更慢。 如果你想要它們,你真正想要的是一個全局的。 函數靜態插入代碼/數據以檢查事物是否已被初始化,每次調用函數時都會運行。

分析可能看不到差異,反匯編和知道要查找的內容可能會有所不同。

我懷疑每個循環只會得到幾個時鍾周期的變化(平均取決於編譯器等)。 有時變化會顯着改善或顯着變慢,這不一定是因為變量 home 已移入/移出堆棧。 假設您在 2ghz 處理器上為 10000 次調用每個函數調用節省了四個時鍾周期。 非常粗略的計算:節省了 20 微秒。 與您當前的執行時間相比,20 微秒是多還是少?

通過將所有 char 和 short 變量轉換為 int 等,您可能會獲得更多的性能改進。 了解微優化是一件好事,但需要花費大量時間來試驗、反匯編、對代碼的執行進行計時,例如,了解更少的指令並不一定意味着更快。

拿你的特定程序,反匯編有問題的函數和調用它的代碼。 有無靜電。 如果您只獲得一兩條指令並且這是您要做的唯一優化,則可能不值得。 您可能無法在分析時看到差異。 例如,在代碼更改之前,緩存行命中位置的更改可能會顯示在分析中。

暫無
暫無

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

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