如果您是.NET性能的粉絲,最近有很多好消息,例如.NET Core 2.1中的性能改進和宣布.NET Core 2.1,但我們還有更多的好消息。分層編譯是一項重要的新特性功能,我們可以作為預覽供任何人試用,從.NET Core 2.1開始。在我們測試的許多場景中,應用程式啟動更快,并且在穩定狀态下運作得更快。一個在.NET Core 2.1上運作的項目,以及對環境變量或項目檔案進行微不足道的更改以啟用它。在本文的其餘部分,我們将介紹它是什麼,如何使用它,以及為什麼它是2.1版本的隐藏技能!
什麼是分層編譯?
從.NET Framework開始,代碼中的每個方法通常都編譯一次。但是,在決定如何進行會影響應用程式性能的編譯時,需要進行權衡。例如,JIT可以進行非常積極的優化并獲得很好的穩定性能,但是優化代碼并不是一件容易的事情,是以您的應用程式啟動速度非常慢。或者JIT可以使用非常簡單的編譯算法,這些算法可以快速運作,是以您的應用程式可以快速啟動,但代碼品質會更差,并且應用程式吞吐量會受到影響。.NET一直試圖采用一種平衡的方法,在啟動和穩定性能方面做得很合理,但使用單一編譯意味着需要妥協。
分層編譯功能通過允許運作時熱交換技術對.NET進行多次編譯同一個方法改變了以上前提。兩套機制的分離以便我們可以選擇最适合啟動的技術,選擇最穩定狀态并且在兩者上都表現出更好性能的第二種技術(分層編譯)。在.NET Core 2.1中,這就是Tiered Compilation旨在為您的應用程式做的事情:
- 更快的應用程式啟動時間 - 當應用程式啟動時,它會等待一些MSIL代碼到JIT。分層編譯要求JIT快速生成初始編譯,如果需要,犧牲代碼品質優化。之後,如果頻繁調用該方法,則在背景線程上生成更優化的代碼,并替換初始代碼以保持應用程式的穩定性能。
- 更快的穩定狀态下的性能 - 對于典型的.NET Core應用程式,大多數架構代碼将從預編譯(ReadyToRun)映像加載。這對于啟動非常有用,但預編譯的映像具有版本控制限制和禁止某些類型優化的CPU指令限制。對于經常調用的這些鏡像中的任何方法,分層編譯請求JIT在背景線程上建立優化代碼,以替換預編譯版本。
更快?到底有多快?
我們将此作為預覽版釋出的部分原因是要了解它對您的應用程式的執行情況,但以下是我們對其進行測試的一些示例。雖然非常依賴于場景,但我們希望這些結果是您在類似工作場景上的典型代表,并且随着功能的成熟,結果将繼續改進。基準測試是在預設配置下運作的.NET Core 2.1 RTM,并且所有數字都經過縮放,是以基準始終為1.0。在第一組中,我們有幾個Tech Empower測試和MusicStore(用來專門測試的項目),這是我們常用的ASP.NET應用示例。
雖然我們的一些ASP.NET基準測試得益于特别好(MvcPlaintext RPS超過60% - 哇!),但分層編譯并不特定于ASP.NET。以下是您在日常開發中可能遇到的一些示例.NET Core指令行應用程式:
你的應用程式将如何運作?測量比預測要容易得多,但我們可以提供一些廣泛的經驗法則。
- 啟動改進主要适用于減少管理托管代碼的時間。您可以使用PerfView等工具來确定您的應用花費多少時間。在我們的測試中,jitting花費的時間通常會減少約35%。
- 穩定狀态的改進主要适用于CPU綁定的應用程式,其中一些熱代碼來自.NET或ASP.NET預編譯庫。例如PerfView可以幫助您确定您的應用程式是這一類。
嘗試一下
一個小免責聲明,該功能仍然是一個預覽。我們已對其進行了大量測試,但預設情況下未啟用此功能,因為我們希望收集回報并繼續進行調整。打開它可能不會使你的應用程式更快,或者你可能遇到我們沒有覆寫到的地方。如果遇到問題,微軟随時為您提供幫助,您可以随時輕松将其禁用。如果您願意,可以在生産中啟用此功能,但我們強烈建議您事先進行測試。
有幾種方式可以選擇加入此功能,所有這些方法都具有相同的效果:
- 如果使用.NET 2.1 SDK 自行建構應用程式 - 将MSBuild屬性<TieredCompilation> true </ TieredCompilation>添加到項目檔案中的預設屬性組。例如:
此GitHub 連結可找到以下代碼
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TieredCompilation>true</TieredCompilation>
</PropertyGroup>
</Project>
- 如果運作已建構的應用程式,請編輯runtimeconfig.json以将System.Runtime.TieredCompilation = true添加到configProperties。例如:
{
"runtimeOptions": {
"configProperties": {
"System.Runtime.TieredCompilation": true
}
},
"framework": {
...
}
}
- 如果您想運作應用程式但不想修改任何檔案,請設定環境變量
COMPlus_TieredCompilation=1
有關嘗試和測量性能的更多詳細資訊,請檢視分層編譯示範
獲得這個技術
好奇它是如何工作的?不要害怕,了解這些内部細節不是使用分層編譯所必需的,如果您願意,可以跳過本節。一目了然,該功能可分為四個不同的部分:
- JIT編譯器可以配置為生成不同品質的彙編代碼 - 令許多人驚訝的是,到目前為止,這還不是該功能的重點。回到.NET的起始,JIT支援預設編譯模式和用于調試的無優化編譯模式。正常模式産生更好的代碼品質并且編譯需要更長時間,而“無優化”模式則相反。對于分層編譯,我們建立了新的配置名稱“Tier0”和“Tier1”,但這些配置生成的代碼與我們一直使用的“無優化”和“正常”模式大緻相同。到目前為止,大多數JIT更改都涉及在請求“Tier0”代碼時使JIT生成代碼更快。我們希望将來繼續提高Tier0編譯速度,
- CodeVersionManager(代碼版本管理)跟蹤同一方法的不同代碼編譯(版本) - 最基本的是一個大記憶體字典,它存儲應用程式中.NET方法之間的映射和不同程式集實作的清單運作時可以使用它來執行該方法。我們使用一些技巧來優化這種資料結構,但如果你想深入研究項目的這個方面,可以參考我們提供的非常好的規範。
- 相同方法的不同彙編代碼彙編之間,在運作時狀态下熱更新的機制, - 當方法A調用方法B時,調用将依賴于jmp指令。通過調整運作時的jmp指令可以控制執行B的哪個實作。
- 決定要建立哪些代碼版本以及何時在它們之間切換的政策 - 運作時始終首先建立Tier0,這是從ReadyToRun映像加載的代碼,或者是使用最小化優化的代碼。呼叫計數器用于确定頻繁運作哪些方法,并使用計時器來避免在啟動期間過早建立Tier1的工作。一旦計數器和計時器都滿足,該方法就會排隊,背景線程會編譯Tier1版本。有關詳細資訊,請檢視規範。
我們從哪裡開始?
分層編譯創造了各種可能性,我們可以繼續充分利用未來的時間。既然運作時可以利用更極端的情況,那我們就有了擴充邊界的動力,既可以加快編譯速度,又可以生成更高品質的代碼。通過代碼的運作時熱更新,.NET可以進行更詳細的分析,然後使用運作時回報來進行更好的優化(配置檔案引導優化)。這些技術可以允許代碼生成器甚至超出無法通路配置檔案資料的最佳靜态優化器。或者還有其他選項,例如用于更好診斷的動态去優化,用于減少記憶體使用的可收集代碼,以及用于性能檢測或服務的熱更新檔。目前,我們最直接的目标仍然接近實際 - 確定預覽中的功能運作良好,響應您的回報,并完成工作的第一次疊代。
總結
我們希望Tiered Compilation為您的應用程式提供與我們的基準測試相同的重大改進,并且我們知道還有更多尚未開發的潛力。請試一試,然後通路github,向我們提供回報,讨論,提問,甚至可以貢獻一些自己的代碼。謝謝!
原文:.NET Core 2.1中的分層編譯(預覽)

作者:Chaunce
來源:http://www.cnblogs.com/xiaoliangge/
GitHub:https://github.com/liuyl1992
個人位址:http://blog.chaunce.top
公衆号請搜:架構師進階俱樂部 SmartLife_com
聲明:原創部落格請在轉載時保留原文連結或者在文章開頭加上本人部落格位址,如發現錯誤,歡迎批評指正。凡是轉載于本人的文章,不能設定打賞功能等盈利行為