共用方式為


最佳化最佳做法

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

編譯程式和連結器選項

特性指引最佳化

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

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

如需詳細資訊,請參閱/GL(整個程序優化)/O1/O2 (最小化大小、最大化速度)。

要使用的優化層級

如果可能的話,應該使用配置文件引導式優化來編譯最終發行組建。 如果無法使用 PGO 進行建置,無論是因為執行已檢測組建的基礎結構不足或無法存取案例,我們建議使用整個程式優化來建置。

參數 /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)

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

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

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

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

如需詳細資訊,請參閱restrict

noalias declspec 也只會套用至函式,並指出函式是半純函式。 半純函式是只參考或修改自變數局部變數、自變數及第一層間接取值的函式。 這個 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++)

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

另請參閱