最佳化最佳做法
本文件說明在 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
是類型限定符,例如 const
或 volatile
,但只針對指標類型。
使用 __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++)。
最後,只針對例外狀況擲回例外狀況。 針對一般控制流程使用例外狀況可能會讓效能受到影響。