Share via


最佳化最佳做法

本檔說明在 Visual Studio 中優化 C++ 程式的一些最佳做法。

編譯器和連結器選項

設定檔引導式優化

Visual Studio 支援 設定檔引導優化 (PGO)。 此優化會使用來自定型執行已檢測應用程式版本的設定檔資料,以推動應用程式的後續優化。 使用 PGO 可能很耗時,因此可能不是每個開發人員所使用的專案,但我們建議使用 PGO 來建置產品的最終版本。 如需詳細資訊,請參閱特性指引最佳化

此外, 神秘le 程式優化 (也稱為連結時間程式碼產生)和 /O1/O2 優化已改善。 一般而言,使用下列其中一個選項編譯的應用程式,會比使用先前編譯器編譯的相同應用程式更快。

如需詳細資訊,請參閱 (神秘le 程式優化] /O1/O2 [最小化大小、最大化速度] /GL

要使用的優化層級

如果可能的話,應該使用設定檔引導式優化來編譯最終發行組建。 如果無法使用 PGO 進行建置,無論是因為執行已檢測組建的基礎結構不足或無法存取案例,建議您使用 神秘le 程式優化進行建置。

參數 /Gy 也非常有用。 它會為每個函式產生個別的 COMDAT,讓連結器在移除未參考的 COMDAT 和 COMDAT 折迭時更有彈性。 使用 /Gy 的唯一缺點是,偵錯時可能會造成問題。 因此,通常建議使用它。 如需詳細資訊,請參閱 /Gy (啟用函式層級連結)。

若要在 64 位環境中連結,建議使用 /OPT:REF,ICF 連結器選項,並在 32 位環境中 /OPT:REF 建議使用連結器選項。 如需詳細資訊,請參閱 /OPT (優化)

強烈建議您產生偵錯符號,即使已優化發行組建也一般。 這不會影響產生的程式碼,而且如有需要,它可讓您更輕鬆地偵錯應用程式。

浮點參數

已移除編譯 /Op 程式選項,並已新增下列四個處理浮點優化編譯器選項:

選項 描述
/fp:precise 這是預設建議,應該在大部分情況下使用。
/fp:fast 如果效能非常重要,建議使用,例如在遊戲中。 這會導致最快的效能。
/fp:strict 如果想要精確的浮點例外狀況和 IEEE 行為,則建議使用。 這會導致效能最慢。
/fp:except[-] 可以與 或 /fp:precise 搭配 /fp:strict 使用,但不能 /fp:fast 使用 。

如需詳細資訊,請參閱 /fp (指定浮點行為)。

優化 declspecs

在本節中,我們將探討兩個可在程式中用來協助效能的 declspec: __declspec(restrict)__declspec(noalias)

restrictdeclspec 只能套用至傳回指標的函式宣告,例如__declspec(restrict) void *malloc(size_t size);

restrictdeclspec 用於傳回未驗證指標的函式上。 這個關鍵字用於 的 malloc C-Runtime 程式庫實作,因為它永遠不會傳回目前程式中已在使用的指標值(除非您正在執行不合法的動作,例如釋放記憶體之後使用記憶體)。

restrictdeclspec 會為編譯器提供執行編譯器優化的詳細資訊。 編譯器最難判斷的其中一件事是哪些指標會別名其他指標,而使用此資訊可大幅協助編譯器。

值得指出的是,這是對編譯器的承諾,而不是編譯器將驗證的內容。 如果您的程式不 restrict 適當地使用此 declspec,您的程式可能會有不正確的行為。

如需詳細資訊,請參閱restrict

noaliasdeclspec 也只會套用至函式,並指出函式是半純函式。 半純函式是只參考或修改引數區域變數、引數及第一層間接取值的函式。 這個 declspec 是編譯器的承諾,如果函式參考指標引數的全域或第二層間接,則編譯器可能會產生中斷應用程式的程式碼。

如需詳細資訊,請參閱noalias

優化 pragmas

也有數個實用的 pragmas 可協助優化程式碼。 我們將討論的第一個是 #pragma optimize

#pragma optimize("{opt-list}", on | off)

這個 pragma 可讓您以函式為基礎設定指定的優化層級。 這適用于使用優化編譯指定函式時,應用程式當機的罕見場合。 您可以使用此功能來關閉單一函式的優化:

#pragma optimize("", off)
int myFunc() {...}
#pragma optimize("", on)

如需詳細資訊,請參閱optimize

內嵌是編譯器執行的最重要優化之一,在這裡我們將討論幾個有助於修改此行為的 pragmas。

#pragma inline_recursion 適用于指定應用程式是否希望應用程式能夠內嵌遞迴呼叫。 預設為關閉。 對於小型函式的淺層遞迴,您可以開啟此功能。 如需詳細資訊,請參閱inline_recursion

另一個限制內嵌深度的實用 pragma 是 #pragma inline_depth 。 當您嘗試限制程式或函式的大小時,這通常很有用。 如需詳細資訊,請參閱inline_depth

__restrict__assume

Visual Studio 中有數個關鍵字可協助效能: __restrict __assume

首先,應該指出 __restrict ,和 __declspec(restrict) 是兩個不同的事情。 雖然它們有些相關,但語意不同。 __restrict 是類型限定詞,例如 constvolatile ,但只針對指標類型。

使用 __restrict 修改的指標稱為 __restrict指標 。 __restrict指標是只能透過__restrict指標存取的指標。 換句話說,另一個指標無法用來存取__restrict指標所指向的資料。

__restrict 可以是 Microsoft C++ 優化工具的強大工具,但請小心使用。 如果不當使用,優化器可能會執行會中斷應用程式的優化。

透過 __assume ,開發人員可以告訴編譯器對某些變數的值進行假設。

例如 __assume(a < 5); ,告訴優化器,在該行程式碼中,變數 a 小於 5。 同樣地,這是對編譯器的承諾。 如果 a 實際上在程式中為 6,則編譯器優化之後的程式列為可能不是您預期的行為。 __assume 在 switch 語句和/或條件運算式之前最有用。

有一些限制 __assume 。 首先,就像 一樣 __restrict ,這只是建議,因此編譯器可以自由地忽略它。 此外, __assume 目前只適用于對常數的可變不平等。 它不會傳播符號不平等,例如假設(a < b)。

內建支援

內建函式是函式呼叫,其中編譯器具有呼叫的內部知識,而不是在程式庫中呼叫函式,而是會發出該函式的程式碼。 intrin.h > 標頭檔 < 包含每個支援硬體平臺的所有可用內建函式。

內建函式可讓程式設計人員深入瞭解程式碼,而不需要使用元件。 使用內建函式有幾個優點:

  • 您的程式碼更容易移植。 多個 CPU 架構提供數個內建函式。

  • 您的程式碼更容易閱讀,因為程式碼仍以 C/C++ 撰寫。

  • 您的程式碼可取得編譯器優化的優點。 編譯器變得更好,內建函式的程式碼產生會改善。

如需詳細資訊,請參閱 編譯器內建函式

例外狀況

使用例外狀況相關聯的效能命中。 使用會禁止編譯器執行特定優化之 try 區塊時,會引入一些限制。 在 x86 平臺上,由於程式碼執行期間必須產生的其他狀態資訊,因此 try 區塊會降低額外的效能。 在 64 位平臺上,try 區塊不會降低效能,但擲回例外狀況後,尋找處理常式和回溯堆疊的程式可能會很昂貴。

因此,建議您避免將 try/catch 區塊引入不需要它的程式碼中。 如果您必須使用例外狀況,請盡可能使用同步例外狀況。 如需詳細資訊,請參閱 Structured Exception Handling (C/C++)

最後,只針對例外狀況擲回例外狀況。 針對一般控制流程使用例外狀況可能會讓效能受到影響。

另請參閱