.NET Core 2.1中的分層編譯(預覽)

若是您是.NET性能的粉絲,最近有不少好消息,例如.NET Core 2.1中的性能改進宣佈.NET Core 2.1,但咱們還有更多的好消息。分層編譯是一項重要的新特性功能,咱們能夠做爲預覽供任何人試用,從.NET Core 2.1開始。在咱們測試的許多場景中,應用程序啓動更快,而且在穩定狀態下運行得更快。一個在.NET Core 2.1上運行的項目,以及對環境變量或項目文件進行微不足道的更改以啓用它。在本文的其他部分,咱們將介紹它是什麼,如何使用它,以及爲何它是2.1版本的隱藏技能!git

什麼是分層編譯?

從.NET Framework開始,代碼中的每一個方法一般都編譯一次。可是,在決定如何進行會影響應用程序性能的編譯時,須要進行權衡。例如,JIT能夠進行很是積極的優化並得到很好的穩定性能,可是優化代碼並非一件容易的事情,所以您的應用程序啓動速度很是慢。或者JIT可使用很是簡單的編譯算法,這些算法能夠快速運行,所以您的應用程序能夠快速啓動,但代碼質量會更差,而且應用程序吞吐量會受到影響。.NET一直試圖採用一種平衡的方法,在啓動和穩定性能方面作得很合理,但使用單一編譯意味着須要妥協。github

分層編譯功能經過容許運行時熱交換技術對.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應用示例。json

 

雖然咱們的一些ASP.NET基準測試得益於特別好(MvcPlaintext RPS超過60% - 哇!),但分層編譯並不特定於ASP.NET。如下是您在平常開發中可能遇到的一些示例.NET Core命令行應用程序:數據結構

你的應用程序將如何運做?測量比預測要容易得多,但咱們能夠提供一些普遍的經驗法則。app

  1. 啓動改進主要適用於減小管理託管代碼的時間。您可使用PerfView工具來肯定您的應用花費多少時間。在咱們的測試中,jitting花費的時間一般會減小約35%。
  2. 穩定狀態的改進主要適用於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中的分層編譯(預覽)

相關文章
相關標籤/搜索