跳至內容

最佳化 C++/編寫高效程式碼/記憶體訪問

來自華夏公益教科書

本節介紹了透過利用處理器快取和作業系統虛擬記憶體管理器進行的二級記憶體交換功能來提高主記憶體訪問效能的指南。

記憶體訪問順序

[編輯 | 編輯原始碼]

按遞增地址順序訪問記憶體。具體來說

  • 按遞增順序掃描陣列;
  • 使用最右邊的索引作為最內層迴圈,掃描多維陣列;
  • 在類建構函式和賦值運算子 (operator=) 中,按宣告順序訪問成員變數。

資料快取最佳化按遞增順序訪問記憶體。

當掃描多維陣列時,最內層迴圈應該遍歷最後一個索引,最內層迴圈應該遍歷倒數第二個索引,依此類推。這樣,可以確保以與記憶體中排列順序相同的順序處理陣列單元。例如,以下程式碼已最佳化

float a[num_levels][num_rows][num_columns];
for (int lev = 0; lev < num_levels; ++lev) {
    for (int r = 0; r < num_rows; ++r) {
         for (int c = 0; c < num_columns; ++c) {
            a[lev][r][c] += 1;
        }
    }
}

記憶體對齊

[編輯 | 編輯原始碼]

保持編譯器預設的記憶體對齊。

預設情況下,編譯器會對基本型別使用對齊標準,在這種標準下,物件只能具有為特定因子的倍數的記憶體地址。這種標準保證了最佳效能,但它可能會在連續物件之間新增 _填充_(或 _空洞_)。

如果需要避免某些結構的填充,則僅在這些結構定義周圍使用 _pragma_ 指令。

在編譯單元中分組函式

[編輯 | 編輯原始碼]

在同一個編譯單元中定義一個類的所有成員函式、該類所有 _friend_ 函式以及該類所有 _friend_ 類的所有成員函式,除非由於檔案大小的原因導致結果檔案變得難以管理。

這樣,從函式編譯得到的機器碼和在類和函式中定義的靜態資料將具有相鄰地址。此外,即使是未執行整個程式最佳化的編譯器也可以最佳化這些函式之間的呼叫。

在編譯單元中分組變數

[編輯 | 編輯原始碼]

在最常使用全域性變數的編譯單元中定義每個全域性變數。

這樣,這些變數將具有彼此相鄰的地址,並且與在這些編譯單元中定義的靜態變數相鄰。此外,即使是未執行整個程式最佳化的編譯器也可以最佳化對這些變數的訪問,來自最常使用它們的函式。

編譯單元中的私有函式和變數

[編輯 | 編輯原始碼]

在匿名名稱空間中宣告對編譯單元全域性的變數和函式,但這些變數和函式未被其他編譯單元使用。

在 C 語言和 C++ 中,這些變數和函式可以宣告為 _static_。但是,在現代 C++ 中,不推薦使用 _static_ 全域性變數和函式,應該用在匿名名稱空間中宣告的變數和函式代替它們。

在這兩種情況下,編譯器都會收到通知,這些識別符號永遠不會被其他編譯單元使用。這允許未執行整個程式最佳化的編譯器最佳化這些變數和函式的使用。

華夏公益教科書