close

點擊上方藍字




關注我們

(本文閱讀時間:15分鐘)

接上篇內容,本篇文章將介紹:DependentHandle 現已公開、RyuJIT、即用型代碼/Crossgen 2、.NET 診斷:EventPipe、SDK 的相關攻略。


DependentHandle 現已公開


該 DependentHandle 類型現在是公共的,具有以下 API 表面:
namespace System.Runtime{ public struct DependentHandle : IDisposable { public DependentHandle(object? target, object? dependent); public bool IsAllocated { get; } public object? Target { get; set; } public object? Dependent { get; set; } public (object? Target, object? Dependent) TargetAndDependent { get; } public void Dispose(); }}
它可用於創建高級系統,例如複雜的緩存系統或 ConditionalWeakTable<TKey, TValue>類型的自定義版本。例如,它將被 MVVM Toolkit 中的 WeakReferenceMessenger 類型使用,以避免在廣播消息時分配內存。
▌可移植線程池
.NET 線程池已作為託管實現重新實現,現在用作 .NET 6 中的默認線程池。我們進行此更改以使所有 .NET 應用程序都可以訪問同一個線程池,而不管是否正在使用 CoreCLR、Mono 或任何其他運行時。作為此更改的一部分,我們沒有觀察到或預期任何功能或性能影響。

RyuJIT


該團隊在此版本中對 .NET JIT 編譯器進行了許多改進,在每個預覽帖子中都有記錄。這些更改中的大多數都提高了性能。這裡介紹了一些 RyuJIT 的亮點。
▌動態 PGO
在 .NET 6 中,我們啟用了兩種形式的 PGO(配置文件引導優化):

動態 PGO 使用從當前運行中收集的數據來優化當前運行。

靜態 PGO 依靠從過去運行中收集的數據來優化未來運行。

動態 PGO 已經在文章前面的性能部分中介紹過。我將提供一個重新上限。
動態 PGO 使 JIT 能夠在運行時收集有關實際用於特定應用程序運行的代碼路徑和類型的信息。然後,JIT 可以根據這些代碼路徑優化代碼,有時會顯着提高性能。我們在測試和生產中都看到了兩位數的健康改進。有一組經典的編譯器技術在沒有 PGO 的情況下使用 JIT 或提前編譯都無法實現。我們現在能夠應用這些技術。熱/冷分離是一種這樣的技術,而去虛擬化是另一種技術。
要啟用動態 PGO,請在應用程序將運行的環境中進行設置DOTNET_TieredPGO=1。
如性能部分所述,動態 PGO 將 TechEmpower JSON「MVC」套件每秒的請求數提高了 26%(510K -> 640K)。這是一個驚人的改進,無需更改代碼。
我們的目標是在未來的 .NET 版本中默認啟用動態 PGO,希望在 .NET 7 中啟用。我們強烈建議您在應用程序中嘗試動態 PGO 並向我們提供反饋。
▌完整的 PGO
要充分利用 Dynamic PGO,您可以設置兩個額外的環境變量:DOTNET_TC_QuickJitForLoops=1和DOTNET_ReadyToRun=0. 這確保了儘可能多的方法參與分層編譯。我們將此變體稱為Full PGO。與動態 PGO 相比,完整 PGO 可以提供更大的穩態性能優勢,但啟動時間會更慢(因為必須在第 0 層運行更多方法)。
您不希望將此選項用於短期運行的無服務器應用程序,但對於長期運行的應用程序可能有意義。
在未來的版本中,我們計劃精簡和簡化這些選項,以便您可以更簡單地獲得完整 PGO 的好處並用於更廣泛的應用程序。
▌靜態 PGO
我們目前使用靜態 PGO來優化 .NET 庫程序集,例如 R2R(Ready To Run)附帶的程序集System.Private.CoreLib。
靜態 PGO 的好處是,在使用 crossgen 將程序集編譯為 R2R 格式時會進行優化。這意味着有運行時的好處而沒有運行時成本。這是非常重要的,也是 PGO 對 C++ 很重要的原因。
▌循環對齊
內存對齊是現代計算中各種操作的共同要求。在 .NET 5 中,我們開始在 32 字節邊界對齊方法。在 .NET 6 中,我們添加了一項執行自適應循環對齊的功能,該功能在具有循環的方法中添加NOP填充指令,以便循環代碼從 mod(16) 或 mod(32) 內存地址開始。這些更改改進並穩定了 .NET 代碼的性能。
在下面的冒泡排序圖中,數據點 1 表示我們開始在 32 字節邊界對齊方法的點。數據點 2 表示我們也開始對齊內部循環的點。如您所見,基準測試的性能和穩定性都有很大提高。
▌硬件加速結構
結構是 CLR 類型系統的重要組成部分。近年來,它們經常被用作整個 .NET 庫中的性能原語。最近的例子ValueTask是ValueTuple和Span<T>。記錄結構是一個新的例子。在 .NET 5 和 .NET 6 中,我們一直在提高結構的性能,部分原因是通過確保結構是局部變量、參數或方法的返回值時可以保存在超快速 CPU 寄存器中)。這對於使用向量計算的 API 特別有用。
▌穩定性能測量
團隊中有大量從未出現在博客上的工程系統工作。這對於您使用的任何硬件或軟件產品都是如此。JIT 團隊開展了一個項目來穩定性能測量,目標是增加我們內部性能實驗室自動化自動報告的回歸值。這個項目很有趣,因為需要進行深入調查和產品更改才能實現穩定性。它還展示了我們為保持和提高績效而衡量的規模。

此圖像演示了不穩定的性能測量,其中性能在連續運行中在慢速和快速之間波動。x 軸是測試日期,y 軸是測試時間,以納秒為單位。到圖表末尾(提交這些更改後),您可以看到測量值穩定,結果最好。這張圖片展示了一個單一的測試。還有更多測試在dotnet/runtime #43227中被證明具有類似的行為。

即用型代碼/Crossgen 2


Crossgen2 是crossgen 工具的替代品。它旨在滿足兩個結果:
讓crossgen開發更高效。
啟用一組目前無法通過 crossgen 實現的功能。
這種轉換有點類似於本機代碼 csc.exe 到託管代碼Roslyn 編譯器。Crossgen2 是用 C# 編寫的,但是它沒有像 Roslyn 那樣公開一個花哨的 API。
我們可能已經/已經為 .NET 6 和 7 計劃了六個項目,這些項目依賴於 crossgen2。矢量指令默認提議是我們希望為 .NET 6 但更可能是 .NET 7 進行的 crossgen2 功能和產品更改的一個很好的例子。版本氣泡是另一個很好的例子。
Crossgen2 支持跨操作系統和架構維度的交叉編譯(因此稱為「crossgen」)。這意味着您將能夠使用單個構建機器為所有目標生成本機代碼,至少與準備運行的代碼相關。但是,運行和測試該代碼是另一回事,為此您需要合適的硬件和操作系統。
第一步是用crossgen2編譯平台本身。我們使用 .NET 6 完成了所有架構的任務。因此,我們能夠在此版本中淘汰舊的 crossgen。請注意,crossgen2 僅適用於 CoreCLR,而不適用於基於 Mono 的應用程序(它們具有一組單獨的代碼生成工具)。
這個項目——至少一開始——並不以性能為導向。目標是啟用更好的架構來託管 RyuJIT(或任何其他)編譯器以離線方式生成代碼(不需要或啟動運行時)。
你可能會說「嘿……如果是用 C# 編寫的,難道你不需要啟動運行時來運行 crossgen2 嗎?」 是的,但這不是本文中「離線」的含義。當 crossgen2 運行時,我們不使用運行 crossgen2 的運行時附帶的 JIT 來生成準備運行 (R2R) 代碼. 那是行不通的,至少對於我們的目標來說是行不通的。想象一下 crossgen2 在 x64 機器上運行,我們需要為 Arm64 生成代碼。Crossgen2 將 Arm64 RyuJIT(針對 x64 編譯)加載為原生插件,然後使用它生成 Arm64 R2R 代碼。機器指令只是保存到文件中的字節流。它也可以在相反的方向工作。在 Arm64 上,crossgen2 可以使用編譯為 Arm64 的 x64 RyuJIT 生成 x64 代碼。我們使用相同的方法來針對 x64 機器上的 x64 代碼。Crossgen2 會加載一個 RyuJIT,它是為任何需要的配置而構建的。這可能看起來很複雜,但如果您想啟用無縫的交叉定位模型,它就是您需要的那種系統,而這正是我們想要的。

我們希望只在一個版本中使用術語「crossgen2」,之後它將替換現有的 crossgen,然後我們將回到使用術語「crossgen」來表示「crossgen2」。

.NET 診斷:EventPipe

EventPipe 是我們用於在進程內或進程外輸出事件、性能數據和計數器的跨平台機制。從 .NET 6 開始,我們已將實現從 C++ 移至 C。通過此更改,Mono 也使用 EventPipe。這意味着 CoreCLR 和 Mono 都使用相同的事件基礎設施,包括 .NET 診斷 CLI 工具。

這一變化還伴隨着 CoreCLR 的小幅減小:
庫大小之後-大小之前差異libcoreclr.so7037856 – 7049408-11552
我們還進行了一些更改,以提高 EventPipe 在負載下的吞吐量。在最初的幾個預覽版中,我們進行了一系列更改,從而使吞吐量提高了 .NET 5 的 2.06 倍:

對於這個基準,越高越好。.NET 6 是橙色線,.NET 5 是藍色線。

SDK


對 .NET SDK 進行了以下改進。

▌.NET 6 SDK 可選工作負載的 CLI 安裝
.NET 6 引入了SDK 工作負載的概念。工作負載是可選組件,可以安裝在 .NET SDK 之上以啟用各種場景。.NET 6 中的新工作負載是:.NET MAUI 和 Blazor WebAssembly AOT 工作負載。我們可能會在 .NET 7 中創建新的工作負載(可能來自現有的 SDK)。工作負載的最大好處是減少大小和可選性。我們希望隨着時間的推移使 SDK 變得更小,並且只安裝您需要的組件。這個模型對開發者機器有好處,對 CI 來說甚至更好。
Visual Studio 用戶並不真正需要擔心工作負載。工作負載功能經過專門設計,以便像 Visual Studio 這樣的安裝協調器可以為您安裝工作負載。可以通過 CLI 直接管理工作負載。
工作負載功能公開了用於管理工作負載的多個動詞,包括以下幾個:

dotnet workload restore— 安裝給定項目所需的工作負載。

dotnet workload install— 安裝命名工作負載。

dotnet workload list— 列出您已安裝的工作負載。

dotnet workload update— 將所有已安裝的工作負載更新到最新的可用版本。

update 動詞查詢更新 nuget.org的工作負載清單、更新本地清單、下載已安裝工作負載的新版本,然後刪除所有舊版本的工作負載。這類似於 apt update && apt upgrade -y(用於基於 Debian 的 Linux 發行版)。將工作負載視為 SDK 的私有包管理器是合理的。它是私有的,因為它僅適用於 SDK 組件。我們將來可能會重新考慮這一點。這些 dotnet workload 命令在給定 SDK 的上下文中運行。假設您同時安裝了 .NET 6 和 .NET 7。工作負載命令將為每個 SDK 提供不同的結果,因為工作負載將不同(至少相同工作負載的不同版本)。
請注意,將 NuGet.org 中的工作負載複製到您的 SDK 安裝中,因此如果 SDK 安裝位置受到保護(即在管理員/根位置),dotnet workload install則需要運行提升或使用sudo。
▌內置 SDK 版本檢查
為了更容易跟蹤 SDK 和運行時的新版本何時可用,我們向 .NET 6 SDK 添加了一個新命令。
dotnet sdk check
它會告訴您是否有可用於您已安裝的任何 .NET SDK、運行時或工作負載的更新版本。您可以在下圖中看到新體驗。

dotnetnew
您現在可以在 NuGet.org 中搜索帶有.dotnetnew--search
模板安裝的其他改進包括支持切換以支持私 有NuGet 源的授權憑據。--interactive

安裝 CLI 模板後,您可以通過和檢查更新是否可用。--update-check--update-apply

▌NuGet 包驗證
包驗證工具使 NuGet 庫開發人員能夠驗證他們的包是否一致且格式正確。
這包括:
驗證版本之間沒有重大更改。
驗證包對於所有特定於運行時的實現是否具有相同的公共 API 集。
確定任何目標框架或運行時適用性差距。
該工具是 SDK 的一部分。使用它的最簡單方法是在項目文件中設置一個新屬性。
<EnablePackageValidation> true </EnablePackageValidation>

▌更多 Roslyn 分析儀

在 .NET 5 中,我們提供了大約 250 個帶有 .NET SDK 的分析器。其中許多已經存在,但作為 NuGet 包在帶外發送。我們為 .NET 6 添加了更多分析器。
默認情況下,大多數新分析器都在信息級別啟用。您可以通過如下配置分析模式在警告級別啟用這些分析器:<AnalysisMode>All</AnalysisMode>
我們為 .NET 6 發布了我們想要的一組分析器(加上一些附加功能),然後將它們中的大多數做成了可供抓取的。社區添加了幾個實現,包括這些。
貢獻者問題標題紐厄爾·克拉克dotnet/運行時 #33777使用基於跨度的string.Concat紐厄爾·克拉克dotnet/運行時 #33784解析時優先string.AsSpan()string.Substring()紐厄爾·克拉克dotnet/運行時 #33789覆蓋Stream.ReadAsync/WriteAsync紐厄爾·克拉克dotnet/運行時 #35343替換為Dictionary<,>.Keys.ContainsContainsKey紐厄爾·克拉克dotnet/運行時 #45552使用代替String.EqualsString.Compare梅克特雷爾dotnet/運行時 #47180使用代替String.Contains(char)String.Contains(String)

感謝 Meik Tranel 和 Newell Clark。

▌為 Platform Compatibility Analyzer 啟用自定義防護
CA1416 平台兼容性分析器已經使用 OperatingSystem 和 RuntimeInformation 中的方法識別平台防護,例如 OperatingSystem.IsWindows 和OperatingSystem.IsWindowsVersionAtLeast。但是,分析器無法識別任何其他保護可能性,例如緩存在字段或屬性中的平台檢查結果,或者在輔助方法中定義了複雜的平台檢查邏輯。
為了允許自定義守衛的可能性,我們添加了新屬性 SupportedOSPlatformGuard並UnsupportedOSPlatformGuard 使用相應的平台名稱和/或版本注釋自定義守衛成員。此注釋被平台兼容性分析器的流分析邏輯識別和尊重。

▌用法

[UnsupportedOSPlatformGuard("browser")] // The platform guard attribute#if TARGET_BROWSER internal bool IsSupported => false;#else internal bool IsSupported => true;#endif [UnsupportedOSPlatform("browser")] void ApiNotSupportedOnBrowser() { } void M1() { ApiNotSupportedOnBrowser(); // Warns: This call site is reachable on all platforms.'ApiNotSupportedOnBrowser()' is unsupported on: 'browser' if (IsSupported) { ApiNotSupportedOnBrowser(); // Not warn } } [SupportedOSPlatform("Windows")] [SupportedOSPlatform("Linux")] void ApiOnlyWorkOnWindowsLinux() { } [SupportedOSPlatformGuard("Linux")] [SupportedOSPlatformGuard("Windows")] private readonly bool _isWindowOrLinux = OperatingSystem.IsLinux() || OperatingSystem.IsWindows(); void M2() { ApiOnlyWorkOnWindowsLinux(); // This call site is reachable on all platforms.'ApiOnlyWorkOnWindowsLinux()' is only supported on: 'Linux', 'Windows'. if (_isWindowOrLinux) { ApiOnlyWorkOnWindowsLinux(); // Not warn } }}

結束


歡迎使用 .NET 6。它是另一個巨大的 .NET 版本,在性能、功能、可用性和安全性方面都有很多的改進。我們希望您能找到許多改進,最終使您在日常開發中更有效率和能力,並提高性能或降低生產中應用程序的成本。我們已經開始從那些已經開始使用 .NET 6 的人那裡聽到好消息。
在 Microsoft,我們還處於 .NET 6 部署的早期階段,一些關鍵應用程序已經投入生產,未來幾周和幾個月內還會有更多應用程序推出。
.NET 6 是我們最新的 LTS 版本。我們鼓勵每個人都轉向它,特別是如果您使用的是 .NET 5。我們期待它成為有史以來採用速度最快的 .NET 版本。
此版本是至少 1000 人(但可能更多)的結果。這包括來自 Microsoft 的 .NET 團隊以及社區中的更多人。我試圖在這篇文章中包含許多社區貢獻的功能。感謝您抽出寶貴時間創建這些內容並完成我們的流程。我希望這次經歷是一次美好的經歷,並且更多的人會做出貢獻。
感謝您成為 .NET 開發人員。
往期精彩回顧

▎.NET 6 攻略大全(一)▎.NET 6 攻略大全(二)


了解更多.NET
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()