友快網

導航選單

visual studio c++編譯器效能測試:測試版本下,效能提升可達可達2到3倍

好訊息一則

我們最近在Visual Studio 的x86/x64 C++編譯器中做了大量效能方面的改進,特別是針對VS預設的除錯版本配置。

以Visual Studio 2019 v16。10 預覽版2為例,我們測試發現,在除錯版本下,效能提升可達2到3倍。這些改進主要是因為我們在執行時檢查(/RTCs)中去掉了一些效能開銷,這個編譯器開關是預設就開啟的。

預設的除錯版本配置

當你在Visual Studio中以除錯模式編譯時,在預設情況下會有一些編譯開關被傳遞到C++編譯器中。今天的這篇文章主要會關注這三個編譯開關:/RTC1, /JMC 和 /ZI。

請不要誤會,這些編譯開關對於除錯來說還是非常有用的,但是加入它們之後會帶來顯著的效能開銷,特別是當啟用了/RTC1開關的時候。在這次的版本釋出中,我們在儘可能地保持編譯器查詢Bug的能力並提供更加平滑的除錯體驗的同時,移除了那些不是那麼必要的效能開銷程式碼。

舉個例子

考察以下程式碼:

下圖是使用了/RTC1 /JMC和/ZI開關後編譯器生成的x64彙編程式碼:

從上面的彙編程式碼可以看出,/JMC和/ZI開關添加了232個額外的位元組到棧上(第5行)。這個棧空間佔用並不是總是必要的。如果同時使用了/RTC1開關,它會嘗試對分配的棧空間進行初始化(第10行),這會導致很多CPU週期的損耗。在這個特殊的例子中,儘管我們分配的棧空間對於/JMC和/ZI功能的執行是必要的,但是對它進行初始化卻不是必要的。我們可以證明,在編譯期,這些檢查並不是必要的。在真實世界中的C++程式碼庫中,還有很多類似這樣的函式程式碼,這就是可以進行效能最佳化的地方。

所以,大概的意思,相信大家基本明白了,如果需要了解更多關於各個開關的深層次含義,請繼續閱讀。

/RTC1

這個開關等效於同時使用/RTCs和/RTCu這兩個開關。/RTCs會使用0xCC來對函式的棧幀進行初始化,然後做各種執行時檢查,檢測未經初始化的本地變數,檢測陣列的溢位以及對棧指標進行有效性驗證(僅x86平臺)。

在上面的彙編程式碼第10行中,指令rep stosd是由/RTCs引入的,這條指令也顯著地降低了效能。如果這個開關和/JMC或者/ZI一起使用,則情況會更糟。

和/JMC開關的互動

/JMC代表”我的程式碼除錯”(Just My Code Debugging)功能,在除錯過程中,它會自動跳過你未編寫的功能(例如框架,庫和其他非使用者程式碼)。它透過在呼叫執行時庫的入口中插入函式呼叫程式碼來進行工作。這有助於偵錯程式區分使用者程式碼和非使用者程式碼。這裡的問題是,將函式呼叫插入到專案中每個函式的入口中,意味著整個專案中不再有葉子函式。如果該功能最初不需要任何堆疊框架,那麼現在將需要,因為根據Windows平臺的AMD64 ABI,我們需要至少有四個用於功能引數的堆疊插槽(稱為Param Home區域)。這意味著/RTC之前未初始化的所有函式,因為它們是葉子函式且沒有堆疊框架,現在將被初始化。在程式中通常有很多葉子函式是正常的,尤其是當你使用大量的模板化程式碼庫(例如C++ STL)時。在這種情況下,/MMC會很高興吃掉你的某些CPU週期。這不適用於x86(32位),因為我們那裡沒有任何引數本機區域。

和/ZI開關的互動

我們將要討論的下一個互動是與/ZI開關的。它使你的程式碼具有“編輯並繼續”支援,這意味著您無需在除錯過程中重新編譯整個程式即可進行小的更改。

為了增加這種支援,我們向堆疊中添加了一些填充位元組(填充位元組的實際數量取決於函式的大小)。 這樣,你可以將在除錯會話期間新增的所有新變數分配在填充區域上,而無需更改總堆疊幀大小,並且你可以繼續除錯而不必重新編譯程式碼。

你可能已經猜到了,更多的堆疊區域意味著更多的內容可以透過/RTC進行初始化,從而導致更多的開銷。

解決方案

所有這些問題的根源是不必要的初始化。我們真的需要每次都進入初始化堆疊區域嗎?不。當確實需要堆疊初始化時,可以在編譯器中安全地證明。例如,當至少有一個地址獲取變數,在函式中宣告的陣列或未初始化的變數時,需要使用它。對於其他所有情況,我們都可以安全地跳過初始化,因為無論如何我們都不會透過執行時檢查詢到任何有用的東西。

當你使用編輯並繼續編譯時,情況會變得更加複雜,因為現在你可以在除錯會話中新增未初始化的變數,只有在初始化堆疊區域時才能檢測到該變數。而且我們可能還沒有做到這一點。為了解決此問題,我們在除錯資訊中包含了必要的位,並透過Debug Interface Access SDK對其進行了公開。此資訊告訴偵錯程式/ZI引入的填充區域在哪裡開始和結束。它還告訴偵錯程式該函式是否需要任何堆疊初始化。如果是這樣,偵錯程式將無條件地初始化此記憶體範圍內的堆疊區域,以供你在除錯會話期間編輯的功能使用。新變數始終分配在此初始化區域的頂部,並且我們的執行時檢查現在可以檢測您新新增的程式碼是否安全。

結果

我們在預設的除錯配置中編譯了以下專案,然後使用生成的可執行檔案來執行測試。 我們注意到,我們嘗試的所有專案的效能都提高了2到3倍。更多使用到了STL庫的專案可能會看到更大的改進。

總結

雖然程式正式釋出是以發行版本(Release)Release)為主,但是提升除錯版本的效能,對於除錯大型程式來說,也是不錯的。

最後

Microsoft Visual C++團隊的部落格是我非常喜歡的部落格之一,裡面有很多關於Visual C++的知識和最新的開發進展。大浪淘沙,如果你對Visual C++這門古老的技術還是那麼感興趣,則可以經常去他們那(或者我這)逛逛。

本文來自:《2x-3x Performance Improvements for Debug Builds》

上一篇:【高手進階】windows系統中預設新建-資料夾的名稱,如何修改預設的名稱
下一篇:蘋果關閉系統驗證通道,致ios 14,5.1降頻和發熱,但沒有解決方法