...

宣布 .NET 6 — 迄今爲(wéi / wèi)止最快的(de) .NET

2021-12-13


2021 年 11 月 8 日 (以(yǐ)下是(shì)譯文)


歡迎使用 .NET 6。今天的(de)發布是(shì) .NET 團隊和(hé / huò)社區一(yī / yì /yí)年多來(lái)努力的(de)結果。C# 10 和(hé / huò) F# 6 提供了(le/liǎo)語言改進,使您的(de)代碼更簡單、更好。性能有了(le/liǎo)巨大(dà)的(de)提升,我們已經看到(dào)降低了(le/liǎo) Microsoft 托管雲服務的(de)成本。.NET 6 是(shì)第一(yī / yì /yí)個(gè)原生支持 Apple Silicon (Arm64) 的(de)版本,并且還針對 Windows Arm64 進行了(le/liǎo)改進。我們構建了(le/liǎo)一(yī / yì /yí)個(gè)新的(de)動态配置文件引導優化 (PGO) 系統,該系統可提供僅在(zài)運行時(shí)才可能進行的(de)深度優化。使用dotnet monitor和(hé / huò)OpenTelemetry改進了(le/liǎo)雲診斷。WebAssembly支持更強大(dà)、更高效。添加了(le/liǎo)新的(de) API,用于(yú)HTTP/3處理 JSON數學,直接操作内存。.NET 6 将得到(dào)三年支持。開發人(rén)員已經開始将應用程序升級到(dào) .NET 6,我們已經在(zài)生産中聽到(dào)了(le/liǎo)很好的(de)早期結果。.NET 6 已爲(wéi / wèi)您的(de)應用做好準備。

您可以(yǐ)下載适用于(yú) Linux、macOS 和(hé / huò) Windows 的(de).NET 6

有關Web 方案的(de)新增功能,請參閱ASP.NET Core帖子(zǐ)。

Visual Studio 2022 也(yě)在(zài)今天發布。閱讀公告觀看發布活動以(yǐ)了(le/liǎo)解有關發布的(de)更多信息。

PowerShell 7.2也(yě)在(zài)今天發布,基于(yú) .NET 6。PowerShell 用戶可以(yǐ)訪問與 .NET 開發人(rén)員相同的(de)性能改進和(hé / huò) API。

.NET Conf是(shì)一(yī / yì /yí)個(gè)爲(wéi / wèi)期三天的(de)免費虛拟開發人(rén)員活動,旨在(zài)慶祝 .NET 的(de)主要(yào / yāo)版本。它将于(yú)明天開始,并于(yú) 11 月 9 日至 11 日舉行,屆時(shí)将有來(lái)自我們團隊、Microsoft 團隊和(hé / huò)更廣泛社區的(de)演講者參加 80 多場會議。收聽學習并與我們互動

查看新的(de)對話帖子(zǐ),就(jiù)最新的(de) .NET 功能進行工程師對工程師的(de)深入讨論。

.NET 6 亮點

.NET 6 是(shì):

該版本包括大(dà)約一(yī / yì /yí)萬個(gè) git 提交。即使這(zhè)篇文章很長,它也(yě)跳過了(le/liǎo)許多改進。您必須下載并試用 .NET 6 才能看到(dào)所有新内容。

支持

.NET 6 是(shì)一(yī / yì /yí)個(gè)長期支持 (LTS) 版本,将支持三年。它支持多種操作系統,包括 macOS Apple Silicon 和(hé / huò) Windows Arm64。

紅帽與 .NET 團隊合作在(zài)紅帽企業 Linux 上(shàng)支持 .NET。在(zài) RHEL 8 及更高版本上(shàng),.NET 6 将可用于(yú) AMD 和(hé / huò) Intel (x64_64)、ARM (aarch64) 以(yǐ)及 IBM Z 和(hé / huò) LinuxONE (s390x) 架構。

請開始将您的(de)應用程序遷移到(dào) .NET 6,尤其是(shì) .NET 5 應用程序。我們從早期采用者那裏聽說(shuō),從 .NET Core 3.1 和(hé / huò) .NET 5 升級到(dào) .NET 6 很簡單。

Visual Studio 2022和(hé / huò)Visual Studio 2022 for Mac支持 .NET 6 。Visual Studio 2019、Visual Studio for Mac 8 或 MSBuild 16 不(bù)支持它。如果要(yào / yāo)使用 .NET 6,則需要(yào / yāo)升級到(dào)Visual Studio 2022(現在(zài)也(yě)是(shì) 64 位)。Visual Studio CodeC# 擴展支持 .NET 6 。

Azure 應用服務:

注意:如果您的(de)應用已在(zài)應用服務上(shàng)運行 .NET 6 預覽版或 RC 構建,則一(yī / yì /yí)旦 .NET 6 運行時(shí)和(hé / huò) SDK 部署到(dào)您所在(zài)的(de)區域,它将在(zài)第一(yī / yì /yí)次重新啓動時(shí)自動更新。如果您部署了(le/liǎo)自包含應用程序,則需要(yào / yāo)重新構建和(hé / huò)重新部署。

統一(yī / yì /yí)擴展平台

.NET 6 爲(wéi / wèi)浏覽器桌面IoT和(hé / huò)移動應用程序提供了(le/liǎo)一(yī / yì /yí)個(gè)統一(yī / yì /yí)的(de)平台。底層平台已更新,以(yǐ)滿足所有應用程序類型的(de)需求,并使您可以(yǐ)輕松地(dì / de)在(zài)所有應用程序中重用代碼。新功能和(hé / huò)改進可同時(shí)用于(yú)所有應用程序,因此您在(zài)雲中或移動設備上(shàng)運行的(de)代碼具有相同的(de)行爲(wéi / wèi)方式并具有相同的(de)優勢。





随着每個(gè)版本的(de)發布,.NET 開發人(rén)員的(de)影響範圍不(bù)斷擴大(dà)。機器學習和(hé / huò)WebAssembly是(shì)最近添加的(de)兩個(gè)。例如,通過機器學習,您可以(yǐ)編寫應用程序來(lái)查找流數據中的(de)異常情況。使用 WebAssembly,您可以(yǐ)在(zài)浏覽器中托管 .NET 應用程序,就(jiù)像 HTML 和(hé / huò) JavaScript 一(yī / yì /yí)樣,或者将它們與 HTML 和(hé / huò) JavaScript 混合使用

最令人(rén)興奮的(de)新增功能之(zhī)一(yī / yì /yí)是(shì).NET 多平台應用程序 UI (.NET MAUI)。您現在(zài)可以(yǐ)在(zài)單個(gè)項目中編寫代碼,從而(ér)提供跨桌面和(hé / huò)移動操作系統的(de)現代客戶端應用程序體驗。.NET MAUI 的(de)發布時(shí)間将比 .NET 6 晚一(yī / yì /yí)點。我們在(zài) .NET MAUI 上(shàng)投入了(le/liǎo)大(dà)量時(shí)間和(hé / huò)精力,很高興能夠發布它并看到(dào) .NET MAUI 應用程序投入生産。

當然,.NET 應用程序也(yě)可以(yǐ)在(zài)Windows 桌面上(shàng)使用(使用Windows 窗體和(hé / huò)WPF)以(yǐ)及在(zài)雲中使用http://ASP.NETCore。它們是(shì)我們提供時(shí)間最長的(de)應用程序類型,并且仍然非常受歡迎,我們在(zài) .NET 6 中對它們進行了(le/liǎo)改進。

面向 .NET 6

繼續以(yǐ)廣泛的(de)平台爲(wéi / wèi)主題,在(zài)所有這(zhè)些操作系統上(shàng)編寫 .NET 代碼很容易。

要(yào / yāo)以(yǐ) .NET 6爲(wéi / wèi)目标,您需要(yào / yāo)使用 .NET 6 目标框架,如下所示:

net6.0

該目标框架名字對象(TFM),您可以(yǐ)訪問所有的(de)跨平台的(de)API,.NET提供。如果您正在(zài)編寫控制台應用程序、http://ASP.NETCore 應用程序或可重用的(de)跨平台庫,這(zhè)是(shì)最佳選擇。net6.0

如果您的(de)目标是(shì)特定的(de)操作系統(例如編寫Windows 窗體或 iOS 應用程序),那麽還有另一(yī / yì /yí)組 TFM(每個(gè)都針對一(yī / yì /yí)個(gè)不(bù)言而(ér)喻的(de)操作系統)供您使用。它們使您可以(yǐ)訪問所有 API以(yǐ)及一(yī / yì /yí)系列特定于(yú)操作系統的(de) API 。net6.0

  • net6.0-android

  • net6.0-ios

  • net6.0-maccatalyst

  • net6.0-tvos

  • net6.0-windows

每個(gè)無版本的(de) TFM 都相當于(yú)針對 .NET 6 支持的(de)最低操作系統版本。如果您想要(yào / yāo)特定或訪問更新的(de) API,可以(yǐ)指定操作系統版本。

的(de)和(hé / huò)TFMS支持(同.NET 5)。Android 和(hé / huò) Apple TFM 是(shì) .NET 6 的(de)新增功能,目前處于(yú)預覽階段。稍後的(de) .NET 6 更新将支持它們。net6.0net6.0-windows

操作系統特定的(de) TFM 之(zhī)間沒有兼容性關系。例如,與. 如果您想共享代碼,您需要(yào / yāo)使用帶有語句的(de)源代碼或帶有目标代碼的(de)二進制文件來(lái)實現。net6.0-iosnet6.0-tvos#ifnet6.0

表現

自從我們啓動 .NET Core 項目以(yǐ)來(lái),該團隊就(jiù)一(yī / yì /yí)直非常關注性能。Stephen Toub在(zài)捕捉每個(gè)版本的(de) .NET 性能進展方面做得非常出(chū)色。如果您還沒有機會,我建議您查看他(tā)在(zài) .NET 6 中的(de)性能改進帖子(zǐ)。

在(zài)這(zhè)篇博文中,我收集了(le/liǎo)一(yī / yì /yí)些您想了(le/liǎo)解的(de)重大(dà)性能改進,包括文件 IO、界面轉換、PGO 和(hé / huò) System.Text.Json。

動态 PGO

動态輪廓引導優化 (PGO)可以(yǐ)顯着提高穩态性能。例如,PGO 使 TechEmpower JSON“MVC”套件的(de)每秒請求數提高了(le/liǎo) 26%(510K -> 640K)。

動态 PGO 建立在(zài)分層編譯之(zhī)上(shàng),它使方法能夠首先非常快速地(dì / de)編譯(稱爲(wéi / wèi)“第 0 層”)以(yǐ)提高啓動性能,然後在(zài)啓用大(dà)量優化的(de)情況下随後重新編譯(稱爲(wéi / wèi)“第 1 層”)一(yī / yì /yí)旦這(zhè)種方法被證明是(shì)有效的(de)。該模型使方法能夠在(zài)第 0 層中進行檢測,以(yǐ)允許對代碼的(de)執行進行各種觀察。當這(zhè)些方法在(zài)第 1 層重新編譯時(shí),從第 0 層執行中收集的(de)信息将用于(yú)更好地(dì / de)優化第 1 層代碼。這(zhè)就(jiù)是(shì)機制的(de)本質。

動态 PGO 的(de)啓動時(shí)間将比默認運行時(shí)稍慢,因爲(wéi / wèi)在(zài)第 0 層方法中運行額外的(de)代碼來(lái)觀察方法行爲(wéi / wèi)。

要(yào / yāo)啓用動态 PGO,請在(zài)您的(de)應用程序将運行的(de)環境中進行設置。您還必須确保啓用分層編譯(默認情況下)。動态 PGO 是(shì)可選的(de),因爲(wéi / wèi)它是(shì)一(yī / yì /yí)種新的(de)、有影響力的(de)技術。我們希望發布選擇性使用和(hé / huò)相關反饋,以(yǐ)确保它經過全面壓力測試。我們對分層編譯做了(le/liǎo)同樣的(de)事情。至少一(yī / yì /yí)個(gè)非常大(dà)的(de) Microsoft 服務支持動态 PGO,并且已經在(zài)生産中使用它。我們鼓勵您嘗試一(yī / yì /yí)下。DOTNET_TieredPGO=1

您可以(yǐ)在(zài) .NET 6中的(de)性能博文中看到(dào)更多關于(yú)動态 PGO 優勢的(de)信息,包括以(yǐ)下微基準測試,它測量特定 LINQ 枚舉器的(de)成本。

private IEnumerator _source = Enumerable.Range(0, long.MaxValue).GetEnumerator();

[Benchmark]
public void MoveNext() => _source.MoveNext();

這(zhè)是(shì)有和(hé / huò)沒有動态 PGO 的(de)結果。




這(zhè)是(shì)一(yī / yì /yí)個(gè)相當大(dà)的(de)差異,但也(yě)增加了(le/liǎo)代碼大(dà)小,這(zhè)可能會讓一(yī / yì /yí)些讀者感到(dào)驚訝。這(zhè)是(shì) JIT 生成的(de)彙編代碼的(de)大(dà)小,而(ér)不(bù)是(shì)内存分配(這(zhè)是(shì)一(yī / yì /yí)個(gè)更常見的(de)焦點)。.NET 6 Performance 帖子(zǐ)對此有很好的(de)解釋。

PGO 實現中常見的(de)一(yī / yì /yí)種優化是(shì)“熱/冷拆分”,其中經常執行的(de)方法部分(“熱”)在(zài)方法開始時(shí)靠近在(zài)一(yī / yì /yí)起,而(ér)不(bù)經常執行的(de)方法部分(“冷”)被移到(dào)一(yī / yì /yí)起移動到(dào)方法的(de)末尾。這(zhè)可以(yǐ)更好地(dì / de)使用指令緩存并最大(dà)限度地(dì / de)減少可能未使用的(de)代碼的(de)負載。

作爲(wéi / wèi)上(shàng)下文,接口調度是(shì) .NET 中最昂貴的(de)調用類型。非虛拟方法調用是(shì)最快的(de),甚至更快的(de)是(shì)可以(yǐ)通過内聯消除的(de)調用。在(zài)這(zhè)種情況下,動态 PGO 爲(wéi / wèi)MoveNext. 第一(yī / yì /yí)個(gè) — 熱的(de) — 是(shì)直接調用,另一(yī / yì /yí)個(gè) — 冷的(de) — 是(shì)通過. 如果最熱門的(de)人(rén)大(dà)部分時(shí)間都被跟注,那将是(shì)一(yī / yì /yí)場巨大(dà)的(de)勝利。Enumerable+RangeIterator.MoveNextIEnumerator

這(zhè)就(jiù)是(shì)魔法。當 JIT 檢測此方法的(de)第 0 層代碼時(shí),包括檢測此接口分派以(yǐ)跟蹤_source每次調用的(de)具體類型。并且 JIT 發現每次調用都在(zài)一(yī / yì /yí)個(gè)名爲(wéi / wèi) 的(de)類型上(shàng),這(zhè)是(shì)一(yī / yì /yí)個(gè)用于(yú)在(zài)實現内部實現的(de)私有類。因此,對于(yú)第 1 層,JIT 已發出(chū)檢查以(yǐ)查看類型是(shì)否爲(wéi / wèi):如果不(bù)是(shì),則它跳轉到(dào)我們之(zhī)前強調的(de)執行正常接口調度的(de)冷部分。但如果是(shì)——基于(yú)分析數據預計在(zài)絕大(dà)多數時(shí)間都是(shì)這(zhè)種情況——然後它可以(yǐ)繼續直接調用Enumerable+RangeIteratorEnumerable.RangeEnumerable_sourceEnumerable+RangeIteratorEnumerable+RangeIterator.MoveNext方法,非虛拟化。不(bù)僅如此,它還認爲(wéi / wèi)内聯該MoveNext方法是(shì)有利可圖的(de)。最終效果是(shì)生成的(de)彙編代碼稍大(dà)一(yī / yì /yí)些,但針對預期最常見的(de)确切場景進行了(le/liǎo)優化。當我們開始構建動态 PGO 時(shí),這(zhè)些就(jiù)是(shì)我們想要(yào / yāo)的(de)勝利。

動态 PGO 在(zài) RyuJIT 部分再次讨論。

文件 IO 改進

FileStream幾乎完全用 .NET 6 重寫,重點是(shì)提高異步文件 IO 性能。在(zài) Windows 上(shàng),實現不(bù)再使用阻塞 API 并且可以(yǐ)快幾倍!我們還改進了(le/liǎo)所有平台上(shàng)的(de)内存使用。在(zài)第一(yī / yì /yí)個(gè)異步操作(通常分配)之(zhī)後,我們已經使異步操作無分配!此外,當 Windows 和(hé / huò) Unix 實現不(bù)同(并且這(zhè)是(shì)可能的(de))時(shí),我們使邊緣情況的(de)行爲(wéi / wèi)變得統一(yī / yì /yí)。

這(zhè)種重寫的(de)性能改進使所有操作系統受益。Windows 的(de)好處是(shì)最高的(de),因爲(wéi / wèi)它遠遠落後。macOS 和(hé / huò) Linux 用戶還應該看到(dào)顯着的(de)FileStream性能改進。

以(yǐ)下基準測試将 100 MB 寫入新文件。

private byte[] _bytes = new byte[8_000];

[Benchmark]
public async Task Write100MBAsync()
{
    using FileStream fs = new("file.txt", FileMode.Create, FileAccess.Write, FileShare.None, 1, FileOptions.Asynchronous);
    for (int i = 0; i < 100_000_000 / 8_000; i++)
        await fs.WriteAsync(_bytes);
}

在(zài)帶有 SSD 驅動器的(de) Windows 上(shàng),我們觀察到(dào)了(le/liǎo)4 倍的(de)加速和(hé / huò)超過1200 倍的(de)分配下降:

我們還認識到(dào)需要(yào / yāo)更多高性能文件 IO 功能:并發讀寫和(hé / huò)分散/聚集 IO。針對這(zhè)些情況,我們爲(wéi / wèi)和(hé / huò)類引入了(le/liǎo)新的(de) API 。System.IO.FileSystem.IO.RandomAccess

async Task AllOrNothingAsync(string path, IReadOnlyList buffers)
{
    using SafeFileHandle handle = File.OpenHandle(
        path, FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.Asynchronous,
        preallocationSize: buffers.Sum(buffer => buffer.Length)); // hint for the OS to pre-allocate disk space

    await RandomAccess.WriteAsync(handle, buffers, fileOffset: 0); // on Linux it's translated to a single sys-call!
}

示例演示:

預分配大(dà)小功能提高了(le/liǎo)性能,因爲(wéi / wèi)寫入操作不(bù)需要(yào / yāo)擴展文件,而(ér)且文件碎片化的(de)可能性較小。這(zhè)種方法提高了(le/liǎo)可靠性,因爲(wéi / wèi)寫操作将不(bù)再因空間不(bù)足而(ér)失敗,因爲(wéi / wèi)空間已經被保留。Scatter/Gather IO API 減少了(le/liǎo)寫入數據所需的(de)系統調用次數。

更快的(de)界面檢查和(hé / huò)轉換

界面投射性能提升了(le/liǎo) 16% – 38%。這(zhè)種改進對于(yú) C# 與接口之(zhī)間的(de)模式匹配特别有用。





該圖表展示了(le/liǎo)代表性基準的(de)改進規模。

将 .NET 運行時(shí)的(de)一(yī / yì /yí)部分從 C++ 遷移到(dào)托管 C# 的(de)最大(dà)優勢之(zhī)一(yī / yì /yí)是(shì)它降低了(le/liǎo)貢獻的(de)障礙。這(zhè)包括接口轉換,它作爲(wéi / wèi) .NET 6 的(de)早期更改移至 C#。.NET 生态系統中通曉 C# 的(de)人(rén)多于(yú) C++(并且運行時(shí)使用具有挑戰性的(de) C++ 模式)。能夠閱讀組成運行時(shí)的(de)一(yī / yì /yí)些代碼是(shì)培養對以(yǐ)各種形式貢獻的(de)信心的(de)重要(yào / yāo)一(yī / yì /yí)步。

歸功于(yú)本·亞當斯

System.Text.Json 源代碼生成器

我們爲(wéi / wèi) System.Text.Json添加了(le/liǎo)一(yī / yì /yí)個(gè)源代碼生成器,它避免了(le/liǎo)在(zài)運行時(shí)進行反射和(hé / huò)代碼生成的(de)需要(yào / yāo),并且可以(yǐ)在(zài)構建時(shí)生成最佳序列化代碼。序列化程序通常使用非常保守的(de)技術編寫,因爲(wéi / wèi)它們必須如此。但是(shì),如果您閱讀自己的(de)序列化源代碼(使用序列化程序),您會看到(dào)哪些明顯的(de)選擇可以(yǐ)使序列化程序在(zài)您的(de)特定情況下更加優化。這(zhè)正是(shì)這(zhè)個(gè)新的(de)源生成器所做的(de)。

除了(le/liǎo)提高性能和(hé / huò)減少内存之(zhī)外,源代碼生成器還可以(yǐ)生成最适合程序集修整的(de)代碼。這(zhè)有助于(yú)開發更小的(de)應用程序。

序列化POCO是(shì)一(yī / yì /yí)個(gè)非常常見的(de)場景。使用新源發生器,我們觀察到(dào),序列化是(shì)〜1.6倍快與我們的(de)基準

TechEmpower緩存基準行使平台或從數據庫來(lái)源的(de)信息架構的(de)内存緩存。基準測試的(de) .NET 實現對緩存數據執行 JSON 序列化,以(yǐ)便将其作爲(wéi / wèi)對測試工具的(de)響應發送。

我們觀察到(dào)~100K RPS 增益(~40% 增加)。與MemoryCache 性能改進相結合時(shí),.NET 6 的(de)吞吐量比 .NET 5 高 50% !

C# 10

歡迎使用 C# 10。C# 10 的(de)一(yī / yì /yí)個(gè)主要(yào / yāo)主題是(shì)繼續從C# 9 中的(de)頂級語句開始的(de)簡化之(zhī)旅。新功能從 中删除了(le/liǎo)更多的(de)儀式,導緻程序短至一(yī / yì /yí)行。他(tā)們的(de)靈感來(lái)自與沒有 C# 經驗的(de)人(rén)(學生、專業開發人(rén)員和(hé / huò)其他(tā)人(rén))交談,并學習對他(tā)們來(lái)說(shuō)最有效且直觀的(de)方法。Program.cs

大(dà)多數.NET SDK 模闆已經更新,以(yǐ)提供 C# 10 現在(zài)可以(yǐ)實現的(de)更簡單、更簡潔的(de)體驗。我們聽到(dào)反饋說(shuō)有些人(rén)不(bù)喜歡新模闆,因爲(wéi / wèi)它們不(bù)是(shì)爲(wéi / wèi)專家設計的(de),删除面向對象,删除在(zài)編寫 C# 的(de)第一(yī / yì /yí)天就(jiù)需要(yào / yāo)學習的(de)重要(yào / yāo)概念,或鼓勵在(zài)一(yī / yì /yí)個(gè)文件中編寫整個(gè)程序。客觀地(dì / de)說(shuō),這(zhè)些觀點都不(bù)是(shì)真的(de)。新模型同樣适用于(yú)學生和(hé / huò)專業開發人(rén)員。然而(ér),它與我們在(zài) .NET 6 之(zhī)前擁有的(de) C 派生模型不(bù)同。

C# 10 中還有其他(tā)一(yī / yì /yí)些功能和(hé / huò)改進,包括記錄結構。

全局 using 指令

全局 using 指令讓您using隻需指定一(yī / yì /yí)次指令,并将其應用于(yú)您編譯的(de)每個(gè)文件。

以(yǐ)下示例顯示了(le/liǎo)語法的(de)廣度:

  • global using System;

  • global using static System.Console;

  • global using Env = System.Environment;

您可以(yǐ)将語句放在(zài)任何文件中,包括在(zài).global using.csProgram.cs

隐式 using 是(shì)一(yī / yì /yí)個(gè) MSBuild 概念,它根據 SDK自動添加一(yī / yì /yí)組指令。例如,控制台應用隐式使用不(bù)同于(yú)http://ASP.NETCore。global using

隐式使用是(shì)選擇加入的(de),并在(zài)以(yǐ)下位置啓用PropertyGroup:

  • enable

隐式使用是(shì)現有項目的(de)選擇加入,但默認情況下包含在(zài)新的(de) C# 項目中。有關更多信息,請參閱隐式使用

文件範圍的(de)命名空間

文件範圍的(de)命名空間使您可以(yǐ)爲(wéi / wèi)整個(gè)文件聲明命名空間,而(ér)無需将其餘内容嵌套在(zài). 隻允許一(yī / yì /yí)個(gè),并且它必須出(chū)現在(zài)聲明任何類型之(zhī)前。{ ... }

新語法是(shì)一(yī / yì /yí)行:

namespace MyNamespace;

class MyClass { ... } // Not indented

這(zhè)種新語法是(shì)三行縮進樣式的(de)替代:

namespace MyNamespace
{
    class MyClass { ... } // Everything is indented
}

好處是(shì)在(zài)整個(gè)文件都在(zài)同一(yī / yì /yí)個(gè)命名空間中的(de)極其常見的(de)情況下減少縮進。

記錄結構

C# 9 引入了(le/liǎo)記錄作爲(wéi / wèi)類的(de)一(yī / yì /yí)種特殊的(de)面向值的(de)形式。在(zài) C# 10 中,您還可以(yǐ)聲明結構記錄。C# 中的(de)結構已經具有值相等性,但記錄結構添加了(le/liǎo)一(yī / yì /yí)個(gè)==運算符和(hé / huò)一(yī / yì /yí)個(gè) 的(de)實現,以(yǐ)及一(yī / yì /yí)個(gè)基于(yú)值的(de)實現:IEquatableToString

public record struct Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

就(jiù)像記錄類一(yī / yì /yí)樣,記錄結構可以(yǐ)是(shì)“位置”的(de),這(zhè)意味着它們有一(yī / yì /yí)個(gè)主構造函數,它隐式聲明了(le/liǎo)與參數對應的(de)公共成員:

public record struct Person(string FirstName, string LastName);

但是(shì),與記錄類不(bù)同,隐式公共成員是(shì)可變的(de)自動實現的(de)屬性。這(zhè)是(shì)因爲(wéi / wèi)記錄結構是(shì)元組的(de)自然成長故事。例如,如果您有一(yī / yì /yí)個(gè)返回類型,并且您想将其擴展爲(wéi / wèi)命名類型,您可以(yǐ)輕松聲明相應的(de)位置結構記錄并維護可變語義。(string FirstName, string LastName)

如果您想要(yào / yāo)一(yī / yì /yí)個(gè)具有隻讀屬性的(de)不(bù)可變記錄,您可以(yǐ)聲明整個(gè)記錄結構readonly(就(jiù)像其他(tā)結構一(yī / yì /yí)樣):

public readonly record struct Person(string FirstName, string LastName);

C# 10 不(bù)僅支持with記錄結構的(de)表達式,還支持所有結構以(yǐ)及匿名類型的(de)表達式:

var updatedPerson = person with { FirstName = "Mary" };

F# 6

F# 6旨在(zài)讓 F# 更簡單、更高效。這(zhè)适用于(yú)語言設計、庫和(hé / huò)工具。我們對 F# 6(及更高版本)的(de)目标是(shì)消除語言中讓用戶感到(dào)驚訝或對學習 F# 造成障礙的(de)極端情況。我們很高興與 F# 社區合作進行這(zhè)項持續的(de)工作。

使 F# 更快、更具互操作性

新語法直接創建一(yī / yì /yí)個(gè)任務并啓動它。這(zhè)是(shì) F# 6 中最重要(yào / yāo)的(de)特性之(zhī)一(yī / yì /yí),它使異步任務更簡單、性能更高,并且與 C# 和(hé / huò)其他(tā) .NET 語言的(de)互操作性更強。以(yǐ)前,創建 .NET 任務需要(yào / yāo)使用創建任務和(hé / huò)調用.task {…}async {…}Async.StartImmediateAsTask

該功能建立在(zài)稱爲(wéi / wèi)“可恢複代碼”RFC FS-1087的(de)基礎之(zhī)上(shàng)。可恢複代碼是(shì)一(yī / yì /yí)個(gè)核心特性,我們希望在(zài)未來(lái)使用它來(lái)構建其他(tā)高性能異步和(hé / huò)屈服狀态機。task {…}

F# 6 還爲(wéi / wèi)庫作者添加了(le/liǎo)其他(tā)性能特性,包括InlineIfLambdaF# 活動模式的(de)未裝箱表示。一(yī / yì /yí)個(gè)特别顯着的(de)性能改進是(shì)在(zài)列表和(hé / huò)數組表達式的(de)編譯中,現在(zài)它們的(de)速度提高了(le/liǎo)4 倍,并且調試也(yě)更好更簡單。

使 F# 更易于(yú)學習且更統一(yī / yì /yí)

F# 6 啓用索引語法。到(dào)目前爲(wéi / wèi)止,F# 一(yī / yì /yí)直使用 expr.[idx] 進行索引。删除點符号是(shì)基于(yú)第一(yī / yì /yí)次使用 F# 用戶的(de)反複反饋,即使用點是(shì)與他(tā)們期望的(de)标準實踐的(de)不(bù)必要(yào / yāo)的(de)分歧。在(zài)新代碼中,我們建議系統地(dì / de)使用新的(de)索引語法。作爲(wéi / wèi)一(yī / yì /yí)個(gè)社區,我們都應該改用這(zhè)種語法。expr[idx]expr[idx]

F# 社區做出(chū)了(le/liǎo)重要(yào / yāo)改進,使 F# 語言在(zài) F# 6 中更加統一(yī / yì /yí)。其中最重要(yào / yāo)的(de)是(shì)消除了(le/liǎo) F# 縮進規則中的(de)許多不(bù)一(yī / yì /yí)緻和(hé / huò)限制。其他(tā)使 F# 更加統一(yī / yì /yí)的(de)設計添加包括添加as模式;在(zài)計算表達式中允許“重載自定義操作”(對 DSL 有用);允許_丢棄use綁定并允許輸出(chū)中的(de)二進制格式。F# 核心庫添加了(le/liǎo)用于(yú)對列表、數組和(hé / huò)序列進行複制和(hé / huò)更新的(de)新函數,以(yǐ)及其他(tā)内在(zài)函數。從 2.0 開始不(bù)推薦使用的(de) F# 的(de)一(yī / yì /yí)些遺留功能現在(zài)會導緻錯誤。其中許多更改更好地(dì / de)使 F# 符合您的(de)期望,從而(ér)減少了(le/liǎo)意外。%BNativePtr

F# 6 還添加了(le/liǎo)對 F# 中其他(tā)“隐式”和(hé / huò)“類型導向”轉換的(de)支持。這(zhè)意味着更少的(de)顯式向上(shàng)轉換,并增加了(le/liǎo)對 .NET 樣式隐式轉換的(de)一(yī / yì /yí)流支持。F# 還進行了(le/liǎo)調整,以(yǐ)更好地(dì / de)适應使用 64 位整數的(de)數字庫時(shí)代,并對 32 位整數進行隐式加寬。

改進 F# 工具

F# 6 中的(de)工具改進使日常編碼變得更容易。新的(de)“管道(dào)調試”允許您單步執行、設置斷點并檢查 F# 管道(dào)語法的(de)中間值。陰影值的(de)調試顯示已得到(dào)改進,消除了(le/liǎo)調試時(shí)常見的(de)混淆源。現在(zài),F# 工具的(de)性能也(yě)更高,F# 編譯器并行執行解析階段。F# IDE 工具也(yě)得到(dào)了(le/liǎo)改進。F# 腳本現在(zài)更加強大(dà),允許您通過文件固定使用的(de) .NET SDK 版本。input |> f1 |> f2global.json

熱重載

熱重載是(shì)另一(yī / yì /yí)個(gè)性能特性,專注于(yú)開發人(rén)員的(de)生産力。它使您能夠對正在(zài)運行的(de)應用程序進行各種代碼編輯,從而(ér)減少您等待應用程序重新構建、重新啓動或重新導航到(dào)進行代碼更改後所在(zài)位置所需的(de)時(shí)間。

熱重載可通過dotnet watchCLI 工具和(hé / huò) Visual Studio 2022 使用。您可以(yǐ)将熱重載用于(yú)多種應用程序類型,例如 ASP.NET Core、Blazor、.NET MAUI、控制台、Windows 窗體 (WinForms)、WPF、WinUI 3、Azure 函數等。

使用 CLI 時(shí),隻需使用 啓動您的(de) .NET 6 應用程序dotnet watch,進行任何支持的(de)編輯,并在(zài)保存文件時(shí)(如在(zài) Visual Studio Code 中)将立即應用這(zhè)些更改。如果不(bù)支持更改,詳細信息将記錄到(dào)命令窗口。





此圖像顯示正在(zài)啓動的(de) MVC 應用程序dotnet watch。我對和(hé / huò)文件進行了(le/liǎo)編輯(如日志中所報告的(de)那樣),并且兩者都被應用到(dào)代碼中并在(zài)不(bù)到(dào)半秒的(de)時(shí)間内很快地(dì / de)反映在(zài)浏覽器中。.cs.cshtml

使用 Visual Studio 2022 時(shí),隻需啓動您的(de)應用程序,進行受支持的(de)更改,然後使用新的(de)“熱重載”按鈕(如下圖所示)應用這(zhè)些更改。您還可以(yǐ)選擇通過同一(yī / yì /yí)按鈕上(shàng)的(de)下拉菜單在(zài)保存時(shí)應用更改。使用 Visual Studio 2022 時(shí),Hot Reload 可用于(yú)多個(gè) .NET 版本、.NET 5+、.NET Core 和(hé / huò) .NET Framework。例如,您将能夠對OnClickEvent按鈕的(de)處理程序進行代碼隐藏更改。Main應用程序的(de)方法不(bù)支持它。





注意:RuntimeInformation.FrameworkDescription中存在(zài)一(yī / yì /yí)個(gè)錯誤,錯誤在(zài)該圖像中顯示,将很快修複。

熱重載還與現有的(de)“編輯并繼續”功能(在(zài)斷點處停止時(shí))和(hé / huò) XAML 熱重載協同工作,用于(yú)實時(shí)編輯應用程序 UI。目前支持 C# 和(hé / huò) Visual Basic 應用程序(不(bù)是(shì) F#)。

安全

.NET 6 中的(de)安全性得到(dào)了(le/liǎo)顯着改善。它始終是(shì)團隊的(de)重要(yào / yāo)關注點,包括威脅建模、加密和(hé / huò)縱深防禦緩解措施。

在(zài) Linux 上(shàng),我們依賴OpenSSL進行所有加密操作,包括 TLS(HTTPS 需要(yào / yāo))。在(zài) macOS 和(hé / huò) Windows 上(shàng),我們依賴操作系統提供的(de)功能來(lái)實現相同的(de)目的(de)。對于(yú) .NET 的(de)每個(gè)新版本,我們經常需要(yào / yāo)添加對新版本 OpenSSL 的(de)支持。.NET 6 添加了(le/liǎo)對OpenSSL 3 的(de)支持。

OpenSSL 3 的(de)最大(dà)變化是(shì)改進的(de)FIPS 140-2模塊和(hé / huò)更簡單的(de)許可。

.NET 6 需要(yào / yāo) OpenSSL 1.1 或更高版本,并且更喜歡它可以(yǐ)找到(dào)的(de)最高安裝版本的(de) OpenSSL,直到(dào)并包括 v3。在(zài)一(yī / yì /yí)般情況下,當您使用的(de) Linux 發行版切換爲(wéi / wèi)默認設置時(shí),您最有可能開始使用 OpenSSL 3。大(dà)多數發行版還沒有這(zhè)樣做。例如,如果您在(zài) Red Hat 8 或 Ubuntu 20.04 上(shàng)安裝 .NET 6,您将不(bù)會(在(zài)撰寫本文時(shí))開始使用 OpenSSL 3。

OpenSSL 3、Windows 10 21H1 和(hé / huò) Windows Server 2022 都支持ChaCha20Poly1305。您可以(yǐ)在(zài) .NET 6 中使用這(zhè)種新的(de)經過身份驗證的(de)加密方案(假設您的(de)環境支持它)。

感謝凱文-瓊斯爲(wéi / wèi)ChaCha20Poly1305 Linux支持。

我們還發布了(le/liǎo)新的(de)運行時(shí)安全緩解路線圖。重要(yào / yāo)的(de)是(shì)您使用的(de)運行時(shí)不(bù)受教科書式攻擊類型的(de)影響。我們正在(zài)滿足這(zhè)種需求。在(zài) .NET 6 中,我們構建了(le/liǎo)W^X和(hé / huò)英特爾控制流執行技術 (CET) 的(de)初始實現。W^X 完全受支持,默認情況下爲(wéi / wèi) macOS Arm64 啓用,并在(zài)其他(tā)環境中選擇加入。CET 是(shì)所有環境的(de)選擇和(hé / huò)預覽。我們希望默認情況下爲(wéi / wèi) .NET 7 中的(de)所有環境啓用這(zhè)兩種技術。

ARM64

如今,對于(yú)筆記本電腦、雲硬件和(hé / huò)其他(tā)設備,Arm64 令人(rén)興奮不(bù)已。我們對 .NET 團隊也(yě)感到(dào)同樣的(de)興奮,并正在(zài)盡最大(dà)努力跟上(shàng)這(zhè)一(yī / yì /yí)行業趨勢。我們直接與 Arm Holdings、Apple 和(hé / huò) Microsoft 的(de)工程師合作,以(yǐ)确保我們的(de)實施正确且經過優化,并且我們的(de)計劃保持一(yī / yì /yí)緻。這(zhè)些密切的(de)夥伴關系對我們幫助很大(dà)。

  • 特别感謝 Apple 在(zài) M1 芯片發布之(zhī)前向我們的(de)團隊發送了(le/liǎo)一(yī / yì /yí)蒲式耳的(de) Arm64 開發套件,并提供了(le/liǎo)重要(yào / yāo)的(de)技術支持。

  • 特别感謝 Arm Holdings,他(tā)們的(de)工程師代碼審查了(le/liǎo)我們的(de) Arm64 更改并進行了(le/liǎo)性能改進。

在(zài)此之(zhī)前,我們通過 .NET Core 3.0 和(hé / huò) Arm32 添加了(le/liǎo)對 Arm64 的(de)初始支持。該團隊在(zài)最近的(de)幾個(gè)版本中都對 Arm64 進行了(le/liǎo)重大(dà)投資,并且在(zài)可預見的(de)未來(lái)還将繼續。在(zài) .NET 6 中,我們的(de)主要(yào / yāo)重點是(shì)在(zài)macOS 和(hé / huò) Windows Arm64 操作系統上(shàng)支持新的(de) Apple Silicon 芯片和(hé / huò)x64 仿真場景

您可以(yǐ)在(zài) macOS 11+ 和(hé / huò) Windows 11+ Arm64 操作系統上(shàng)安裝 Arm64 和(hé / huò) x64 版本的(de) .NET。我們必須做出(chū)多項設計選擇和(hé / huò)産品更改以(yǐ)确保有效。

我們的(de)策略是(shì)“親原生架構”。我們建議您始終使用與原生架構匹配的(de) SDK,即 macOS 和(hé / huò) Windows Arm64 上(shàng)的(de) Arm64 SDK。SDK 是(shì)一(yī / yì /yí)個(gè)龐大(dà)的(de)軟件體。與仿真相比,在(zài) Arm64 芯片上(shàng)本地(dì / de)運行的(de)性能要(yào / yāo)高得多。我們已更新 CLI 以(yǐ)簡化此操作。我們永遠不(bù)會專注于(yú)優化模拟 x64。

默認情況下,如果您dotnet run使用 Arm64 SDK 的(de) .NET 6 應用程序,它将作爲(wéi / wèi) Arm64 運行。您可以(yǐ)使用參數輕松切換到(dào)以(yǐ) x64 運行,例如. 相同的(de)論點适用于(yú)其他(tā) CLI 動詞。有關詳細信息,請參閱适用于(yú) macOS 和(hé / huò) Windows Arm64 的(de) .NET 6 RC2 更新。-adotnet run -a x64

我想确保涵蓋了(le/liǎo)一(yī / yì /yí)個(gè)微妙之(zhī)處。當您使用 時(shí),SDK 仍以(yǐ) Arm64 的(de)形式在(zài)本機運行。.NET SDK體系結構中存在(zài)進程邊界存在(zài)的(de)固定點。大(dà)多數情況下,一(yī / yì /yí)個(gè)進程必須全是(shì) Arm64 或全是(shì) x64。我稍微簡化了(le/liǎo)一(yī / yì /yí)點,但 .NET CLI 會等待 SDK 架構中的(de)最後一(yī / yì /yí)個(gè)進程創建,然後将它作爲(wéi / wèi)您請求的(de)芯片架構啓動,例如 x64。這(zhè)就(jiù)是(shì)您的(de)代碼運行的(de)過程。這(zhè)樣,您作爲(wéi / wèi)開發人(rén)員可以(yǐ)獲得 Arm64 的(de)好處,但您的(de)代碼可以(yǐ)在(zài)它需要(yào / yāo)的(de)過程中運行。這(zhè)僅在(zài)您需要(yào / yāo)以(yǐ) x64 格式運行某些代碼時(shí)才相關。如果你不(bù)這(zhè)樣做,那麽你可以(yǐ)一(yī / yì /yí)直以(yǐ) Arm64 的(de)方式運行一(yī / yì /yí)切,這(zhè)很好。-a x64

Arm64 支持

以(yǐ)下是(shì)您需要(yào / yāo)了(le/liǎo)解的(de)關鍵點,适用于(yú) macOS 和(hé / huò) Windows Arm64:

  • 支持并推薦 .NET 6 Arm64 和(hé / huò) x64 SDK。

  • 支持所有支持的(de) Arm64 和(hé / huò) x64 運行時(shí)。

  • .NET Core 3.1 和(hé / huò) .NET 5 SDK 可以(yǐ)工作,但提供的(de)功能較少,并且在(zài)某些情況下不(bù)完全受支持。

  • dotnet test尚不(bù)能與 x64 仿真一(yī / yì /yí)起正常工作。我們正在(zài)爲(wéi / wèi)此努力。dotnet test将作爲(wéi / wèi) 6.0.200 版本的(de)一(yī / yì /yí)部分進行改進,可能更早。

有關更完整的(de)信息,請參閱.NET 對 macOS 和(hé / huò) Windows Arm64 的(de)支持

本次讨論中缺少 Linux。它不(bù)像 macOS 和(hé / huò) Windows 那樣支持 x64 仿真。因此,這(zhè)些新的(de) CLI 特性和(hé / huò)支持方法并不(bù)直接适用于(yú) Linux,Linux 也(yě)不(bù)需要(yào / yāo)它們。

Windows  Arm64

我們有一(yī / yì /yí)個(gè)簡單的(de)工具來(lái)演示.NET 運行的(de)環境

C:Usersrich>dotnet tool install -g dotnet-runtimeinfo
You can invoke the tool using the following command: dotnet-runtimeinfo
Tool 'dotnet-runtimeinfo' (version '1.0.5') was successfully installed.

C:Usersrich>dotnet runtimeinfo
         42
         42              ,d                             ,d
         42              42                             42
 ,adPPYb,42  ,adPPYba, MM42MMM 8b,dPPYba,   ,adPPYba, MM42MMM
a8"    `Y42 a8"     "8a  42    42P'   `"8a a8P_____42   42
8b       42 8b       d8  42    42       42 8PP"""""""   42
"8a,   ,d42 "8a,   ,a8"  42,   42       42 "8b,   ,aa   42,
 `"8bbdP"Y8  `"YbbdP"'   "Y428 42       42  `"Ybbd8"'   "Y428

**.NET information
Version: 6.0.0
FrameworkDescription: .NET 6.0.0-rtm.21522.10
Libraries version: 6.0.0-rtm.21522.10
Libraries hash: 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6

**Environment information
ProcessorCount: 8
OSArchitecture: Arm64
OSDescription: Microsoft Windows 10.0.22494
OSVersion: Microsoft Windows NT 10.0.22494.0

如您所見,該工具在(zài) Windows Arm64 上(shàng)本機運行。我将向您展示http://ASP.NETCore 的(de)外觀。





macOS Arm64

并且您可以(yǐ)看到(dào)在(zài) macOS Arm64 上(shàng)的(de)體驗是(shì)相似的(de),并且還展示了(le/liǎo)架構定位。

rich@MacBook-Air app % dotnet --version
6.0.100
rich@MacBook-Air app % dotnet --info | grep RID
 RID:         osx-arm64
rich@MacBook-Air app % cat Program.cs 
using System.Runtime.InteropServices;
using static System.Console;

WriteLine($"Hello, {RuntimeInformation.OSArchitecture} from {RuntimeInformation.FrameworkDescription}!");
rich@MacBook-Air app % dotnet run
Hello, Arm64 from .NET 6.0.0-rtm.21522.10!
rich@MacBook-Air app % dotnet run -a x64
Hello, X64 from .NET 6.0.0-rtm.21522.10!
rich@MacBook-Air app %

該圖展示了(le/liǎo) Arm64 執行是(shì) Arm64 SDK 的(de)默認執行,以(yǐ)及使用參數在(zài)面向 Arm64 和(hé / huò) x64 之(zhī)間切換是(shì)多麽容易。完全相同的(de)體驗适用于(yú) Windows Arm64。-a





此圖像演示了(le/liǎo)相同的(de)内容,但使用http://ASP.NETCore。我使用的(de) .NET 6 Arm64 SDK 與您在(zài)上(shàng)圖中看到(dào)的(de)相同。

Arm64 上(shàng)的(de) Docker

Docker 支持在(zài)本機架構和(hé / huò)仿真中運行的(de)容器,本機架構是(shì)默認的(de)。這(zhè)看起來(lái)很明顯,但當大(dà)多數 Docker Hub 目錄面向 x64 時(shí)可能會令人(rén)困惑。您可以(yǐ)使用來(lái)請求 x64 圖像。--platform linux/amd64

我們僅支持在(zài) Arm64 操作系統上(shàng)運行 Linux Arm64 .NET 容器映像。這(zhè)是(shì)因爲(wéi / wèi)我們從來(lái)不(bù)支持在(zài)QEMU 中運行 .NET ,這(zhè)是(shì) Docker 用于(yú)架構模拟的(de)。看來(lái)這(zhè)可能是(shì)由于(yú) QEMU 的(de)限制





此圖片展示了(le/liǎo)我們維護控制台例子(zǐ):。這(zhè)是(shì)一(yī / yì /yí)個(gè)有趣的(de)示例,因爲(wéi / wèi)它包含一(yī / yì /yí)些用于(yú)打印 CPU 和(hé / huò)内存限制信息的(de)基本邏輯,您可以(yǐ)使用它們。我展示的(de)圖像設置了(le/liǎo) CPU 和(hé / huò)内存限制。mcr.microsoft.com/dotnet/samples

自己試試: docker run --rm mcr.microsoft.com/dotnet/samples

Arm64 性能

Apple Silicon 和(hé / huò) x64 仿真支持項目非常重要(yào / yāo),但是(shì),我們也(yě)普遍提高了(le/liǎo) Arm64 性能。





此圖展示了(le/liǎo)将堆棧幀的(de)内容清零方面的(de)改進,這(zhè)是(shì)一(yī / yì /yí)種常見操作。綠線是(shì)新行爲(wéi / wèi),而(ér)橙色線是(shì)另一(yī / yì /yí)個(gè)(不(bù)太有益的(de))實驗,兩者都相對于(yú)基線有所改進,由藍線表示。對于(yú)這(zhè)個(gè)測試,越低越好。

容器

.NET 6 更适合容器,主要(yào / yāo)基于(yú)本文中讨論的(de)所有改進,适用于(yú) Arm64 和(hé / huò) x64。我們還進行了(le/liǎo)關鍵更改,這(zhè)将有助于(yú)各種場景。使用 .NET 6 驗證容器改進演示了(le/liǎo)其中一(yī / yì /yí)些改進正在(zài)一(yī / yì /yí)起測試。

Windows 容器改進和(hé / huò)新的(de)環境變量也(yě)已包含在(zài) 11 月 9 日(明天)發布的(de) 11 月 .NET Framework 4.8 容器更新中。

發行說(shuō)明可在(zài)我們的(de) docker 存儲庫中找到(dào):


Windows Docker

.NET 6 添加了(le/liǎo)對 Windows 進程隔離容器的(de)支持。如果您在(zài) Azure Kubernetes 服務 (AKS) 中使用Windows 容器,則您依賴于(yú)進程隔離的(de)容器。進程隔離容器可以(yǐ)被認爲(wéi / wèi)與 Linux 容器非常相似。Linux 容器使用cgroups,Windows 進程隔離容器使用Job Objects。Windows 還提供 Hyper-V 容器,它通過更大(dà)的(de)虛拟化提供更大(dà)的(de)隔離。Hyper-V 容器在(zài) .NET 6 中沒有變化。

此更改的(de)主要(yào / yāo)價值是(shì)現在(zài)将報告 Windows 進程隔離容器的(de)正确值。如果在(zài) 64 核機器上(shàng)創建 2 核容器,将返回. 在(zài)以(yǐ)前的(de)版本中,此屬性将報告機器上(shàng)的(de)處理器總數,與 Docker CLI、Kubernetes 或其他(tā)容器編排器/運行時(shí)指定的(de)限制無關。該值被 .NET 的(de)各個(gè)部分用于(yú)縮放目的(de),包括 .NET 垃圾收集器(盡管它依賴于(yú)相關的(de)較低級别的(de) API)。社區庫也(yě)依賴此 API 進行擴展。Environment.ProcessorCountEnvironment.ProcessorCount2

我們最近在(zài) AKS 上(shàng)使用大(dà)量 Pod 生産的(de) Windows 容器上(shàng)與客戶一(yī / yì /yí)起驗證了(le/liǎo)這(zhè)項新功能。他(tā)們能夠用50%的(de)内存運行成功(相比,其典型的(de)配置),先前導緻了(le/liǎo)水平OutOfMemoryException和(hé / huò)StackOverflowException例外。他(tā)們沒有花時(shí)間找到(dào)最小内存配置,但我們猜測它明顯低于(yú)他(tā)們典型内存配置的(de) 50%。由于(yú)這(zhè)一(yī / yì /yí)變化,他(tā)們将轉向更便宜的(de) Azure 配置,從而(ér)節省資金。這(zhè)是(shì)一(yī / yì /yí)個(gè)不(bù)錯的(de)、輕松的(de)勝利,隻需升級即可。

優化縮放

我們從用戶那裏聽到(dào)一(yī / yì /yí)些應用程序在(zài)報告正确值時(shí)無法實現最佳縮放。如果這(zhè)聽起來(lái)與您剛剛閱讀的(de) Windows Containers 内容相反,那麽它有點像。.NET 6 現在(zài)提供DOTNET_PROCESSOR_COUNT 環境變量來(lái)手動控制 的(de)值。在(zài)典型用例中,應用程序可能在(zài) 64 核機器上(shàng)配置有 4 個(gè)核,并且在(zài) 8 核或 16 核方面具有最佳擴展性。此環境變量可用于(yú)啓用該縮放。Environment.ProcessorCountEnvironment.ProcessorCount

這(zhè)個(gè)模型可能看起來(lái)很奇怪,其中和(hé / huò)(通過 Docker CLI)值可能不(bù)同。默認情況下,容器運行時(shí)面向核心等效項,而(ér)不(bù)是(shì)實際核心。這(zhè)意味着,當您說(shuō)需要(yào / yāo) 4 個(gè)内核時(shí),您将獲得 4 個(gè)内核的(de)等效 CPU 時(shí)間,但是(shì)您的(de)應用程序可能(理論上(shàng))在(zài)更多内核上(shàng)運行,甚至在(zài)短時(shí)間内在(zài) 64 核機器上(shàng)運行所有 64 個(gè)内核。這(zhè)可能會使您的(de)應用程序在(zài) 4 個(gè)以(yǐ)上(shàng)的(de)線程上(shàng)更好地(dì / de)擴展(繼續示例),并且分配更多可能是(shì)有益的(de)。這(zhè)假設線程分配基于(yú) 的(de)值。如果您選擇設置更高的(de)值,您的(de)應用可能會使用更多内存。對于(yú)某些工作負載,這(zhè)是(shì)一(yī / yì /yí)個(gè)簡單的(de)權衡。至少,這(zhè)是(shì)一(yī / yì /yí)個(gè)您可以(yǐ)測試的(de)新選項。Environment.ProcessorCount--cpusEnvironment.ProcessorCount

Linux 和(hé / huò) Windows 容器均支持此新功能。

Docker 還提供了(le/liǎo) CPU 組功能,您的(de)應用程序可以(yǐ)關聯到(dào)特定的(de)内核。在(zài)這(zhè)種情況下不(bù)推薦使用此功能,因爲(wéi / wèi)應用程序可以(yǐ)訪問的(de)内核數量是(shì)具體定義的(de)。我們還看到(dào)了(le/liǎo)将它與 Hyper-V 容器一(yī / yì /yí)起使用時(shí)的(de)一(yī / yì /yí)些問題,它并不(bù)是(shì)真正适用于(yú)這(zhè)種隔離模式。

Debian 11 “靶心”

我們密切關注Linux 發行版的(de)生命周期和(hé / huò)發布計劃,并嘗試代表您做出(chū)最佳選擇。Debian 是(shì)我們用于(yú)默認 Linux 映像的(de) Linux 發行版。如果您6.0從我們的(de)一(yī / yì /yí)個(gè)容器存儲庫中提取标簽,您将提取一(yī / yì /yí)個(gè) Debian 映像(假設您使用的(de)是(shì) Linux 容器)。對于(yú)每個(gè)新的(de) .NET 版本,我們都會考慮是(shì)否應該采用新的(de) Debian 版本。

作爲(wéi / wèi)政策問題,我們不(bù)會爲(wéi / wèi)了(le/liǎo)我們的(de)便利标簽而(ér)更改 Debian 版本,例如6.0中期發布。如果我們這(zhè)樣做了(le/liǎo),某些應用程序肯定會崩潰。這(zhè)意味着,在(zài)發布之(zhī)初選擇 Debian 版本非常重要(yào / yāo)。此外,這(zhè)些圖像得到(dào)了(le/liǎo)很多使用,主要(yào / yāo)是(shì)因爲(wéi / wèi)它們被“好标簽”引用。

Debian 和(hé / huò) .NET 版本自然不(bù)是(shì)一(yī / yì /yí)起計劃的(de)。當我們開始 .NET 6 時(shí),我們看到(dào) Debian “bullseye”可能會在(zài) 2021 年發布。我們決定從發布之(zhī)初就(jiù)押注于(yú) Bullseye。我們開始使用.NET 6 Preview 1發布基于(yú) Bullseye 的(de)容器映像,并決定不(bù)再回頭。賭注是(shì) .NET 6 版本将在(zài)與 Bullseye 版本的(de)競争中失敗。到(dào) 8 月 8 日,我們仍然不(bù)知道(dào) Bullseye 什麽時(shí)候發貨,在(zài)我們自己的(de)版本發布前三個(gè)月,即 11 月 8 日。我們不(bù)想在(zài)預覽版 Linux 上(shàng)發布生産 .NET 6,但我們堅持到(dào)了(le/liǎo)我們會輸掉這(zhè)場比賽的(de)計劃。

Debian 11 “bullseye”于(yú) 8 月 14 日發布時(shí),我們感到(dào)驚喜。我們輸了(le/liǎo)比賽,但赢了(le/liǎo)賭注。這(zhè)意味着 .NET 6 用戶從第一(yī / yì /yí)天起就(jiù)默認獲得最好和(hé / huò)最新的(de) Debian。我們相信 Debian 11 和(hé / huò) .NET 6 将成爲(wéi / wèi)許多用戶的(de)絕佳組合。對不(bù)起,克星,我們撞到(dào)了(le/liǎo)靶心

較新的(de)發行版在(zài)其軟件包源中包含各種軟件包的(de)較新主要(yào / yāo)版本,并且通常可以(yǐ)更快地(dì / de)獲得CVE 修複。這(zhè)是(shì)對較新内核的(de)補充。新的(de)發行版可以(yǐ)更好地(dì / de)爲(wéi / wèi)用戶服務。

展望未來(lái),不(bù)久我們将開始計劃對Ubuntu 22.04 的(de)支持。Ubuntu是(shì)另一(yī / yì /yí)個(gè) Debian 家族發行版,深受 .NET 開發人(rén)員的(de)歡迎。我們希望爲(wéi / wèi)新的(de) Ubuntu LTS 版本提供當日支持。

Tianon Gravi 緻敬,他(tā)爲(wéi / wèi)社區維護 Debian 映像并在(zài)我們遇到(dào)問題時(shí)幫助我們。

網絡監視器

dotnet monitor是(shì)容器的(de)重要(yào / yāo)診斷工具。它作爲(wéi / wèi) sidecar 容器鏡像已經有一(yī / yì /yí)段時(shí)間了(le/liǎo),但處于(yú)不(bù)受支持的(de)“實驗”狀态。作爲(wéi / wèi) .NET 6 的(de)一(yī / yì /yí)部分,我們将發布一(yī / yì /yí)個(gè)完全支持生産的(de)基于(yú) .NET 6 的(de)dotnet monitor映像

dotnet monitor已被 Azure 應用服務用作其http://ASP.NETCore Linux 診斷體驗的(de)實現細節。這(zhè)是(shì)預期的(de)場景之(zhī)一(yī / yì /yí),建立在(zài) dotnet monitor 之(zhī)上(shàng)以(yǐ)提供更高級别和(hé / huò)更高價值的(de)體驗。

您現在(zài)可以(yǐ)拉取新圖像:

docker pull mcr.microsoft.com/dotnet/monitor:6.0

dotnet monitor使從 .NET 進程訪問診斷信息(日志、跟蹤、進程轉儲)變得更加容易。在(zài)台式機上(shàng)很容易訪問您想要(yào / yāo)的(de)所有診斷信息,但是(shì),例如,那些熟悉的(de)技術在(zài)使用容器的(de)生産中可能不(bù)起作用。dotnet monitor提供了(le/liǎo)一(yī / yì /yí)種統一(yī / yì /yí)的(de)方法來(lái)收集這(zhè)些診斷工件,無論是(shì)在(zài)您的(de)台式機上(shàng)還是(shì)在(zài) Kubernetes 集群中運行。收集這(zhè)些診斷工件有兩種不(bù)同的(de)機制:

  • 用于(yú)臨時(shí)收集工件的(de)HTTP API。當您已經知道(dào)您的(de)應用程序遇到(dào)問題并且您有興趣收集更多信息時(shí),您可以(yǐ)調用這(zhè)些 API 端點。

  • 基于(yú)規則的(de)配置觸發器,用于(yú)始終在(zài)線的(de)工件集合。您可以(yǐ)配置規則以(yǐ)在(zài)滿足所需條件時(shí)收集診斷數據,例如,在(zài)持續高 CPU 時(shí)收集進程轉儲。

dotnet monitor爲(wéi / wèi) .NET 應用程序提供了(le/liǎo)一(yī / yì /yí)個(gè)通用的(de)診斷 API,可以(yǐ)使用任何工具在(zài)任何地(dì / de)方使用。“通用 API”不(bù)是(shì) .NET API,而(ér)是(shì)您可以(yǐ)調用和(hé / huò)查詢的(de) Web API。dotnet monitor包括一(yī / yì /yí)個(gè) ASP.NET Web 服務器,它直接與 .NET 運行時(shí)中的(de)診斷服務器交互并公開數據。的(de)設計dotnet monitor支持生産中的(de)高性能監控和(hé / huò)安全使用,以(yǐ)控制對特權信息的(de)訪問。dotnet monitor通過非互聯網可尋址的(de)unix 域套接字與運行時(shí)交互——跨越容器邊界。該模型通信模型非常适合此用例。

結構化JSON 日志

JSON格式現在(zài)是(shì)默認控制台記錄f="https://hub.docker.com/_/microsoft-dotnet-aspnet">aspnet.NET 6容器圖像。.NET 5 中的(de)默認設置爲(wéi / wèi)簡單的(de)控制台格式化程序。進行此更改是(shì)爲(wéi / wèi)了(le/liǎo)擁有一(yī / yì /yí)個(gè)默認配置,該配置可與依賴于(yú)機器可讀格式(如 JSON)的(de)自動化工具配合使用。

圖像的(de)輸出(chū)現在(zài)如下所示aspnet:

$ docker run --rm -it -p 8000:80 mcr.microsoft.com/dotnet/samples:aspnetapp
{"EventId":60,"LogLevel":"Warning","Category":"Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository","Message":"Storing keys in a directory u0027/root/.aspnet/DataProtection-Keysu0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.","State":{"Message":"Storing keys in a directory u0027/root/.aspnet/DataProtection-Keysu0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.","path":"/root/.aspnet/DataProtection-Keys","{OriginalFormat}":"Storing keys in a directory u0027{path}u0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed."}}
{"EventId":35,"LogLevel":"Warning","Category":"Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager","Message":"No XML encryptor configured. Key {86cafacf-ab57-434a-b09c-66a929ae4fd7} may be persisted to storage in unencrypted form.","State":{"Message":"No XML encryptor configured. Key {86cafacf-ab57-434a-b09c-66a929ae4fd7} may be persisted to storage in unencrypted form.","KeyId":"86cafacf-ab57-434a-b09c-66a929ae4fd7","{OriginalFormat}":"No XML encryptor configured. Key {KeyId:B} may be persisted to storage in unencrypted form."}}
{"EventId":14,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Now listening on: http://[::]:80","State":{"Message":"Now listening on: http://[::]:80","address":"http://[::]:80","{OriginalFormat}":"Now listening on: {address}"}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Application started. Press Ctrlu002BC to shut down.","State":{"Message":"Application started. Press Ctrlu002BC to shut down.","{OriginalFormat}":"Application started. Press Ctrlu002BC to shut down."}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Hosting environment: Production","State":{"Message":"Hosting environment: Production","envName":"Production","{OriginalFormat}":"Hosting environment: {envName}"}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Content root path: /app","State":{"Message":"Content root path: /app","contentRoot":"/app","{OriginalFormat}":"Content root path: {contentRoot}"}}

可以(yǐ)通過設置或取消設置Logging__Console__FormatterName環境變量或通過代碼更改來(lái)更改記錄器格式類型(有關更多詳細信息,請參閱控制台日志格式)。

更改後,您将看到(dào)如下輸出(chū)(就(jiù)像 .NET 5):

$ docker run --rm -it -p 8000:80 -e Logging__Console__FormatterName="" mcr.microsoft.com/dotnet/samples:aspnetapp
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
      Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
      No XML encryptor configured. Key {8d4ddd1d-ccfc-4898-9fe1-3e7403bf23a0} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app

注意:此更改不(bù)會影響開發人(rén)員計算機上(shàng)的(de) .NET SDK,與dotnet run. 此更改特定于(yú)aspnet容器映像。

支持 OpenTelemetry 指标

我們一(yī / yì /yí)直在(zài)爲(wéi / wèi)最近的(de)幾個(gè) .NET 版本添加對 OpenTelemetry 的(de)支持,作爲(wéi / wèi)我們對可觀察性的(de)關注的(de)一(yī / yì /yí)部分。在(zài) .NET 6 中,我們添加了(le/liǎo)對OpenTelemetry Metrics API 的(de)支持。通過添加對 OpenTelemetry 的(de)支持,您的(de)應用程序可以(yǐ)與其他(tā)OpenTelemetry系統無縫互操作。

System.Diagnostics.Metrics是(shì)OpenTelemetry Metrics API 規範的(de) .NET 實現。Metrics API 是(shì)專門爲(wéi / wèi)處理原始測量而(ér)設計的(de),目的(de)是(shì)高效且同時(shí)地(dì / de)生成這(zhè)些測量的(de)連續摘要(yào / yāo)。

API 包括Meter可用于(yú)創建儀器對象的(de)類。這(zhè)些API暴露四個(gè)儀器類:Counter,Histogram,ObservableCounter,并ObservableGauge支持不(bù)同的(de)指标方案。此外,API 公開MeterListener該類以(yǐ)允許偵聽儀器的(de)記錄測量以(yǐ)進行聚合和(hé / huò)分組。

OpenTelemetry .NET實現将擴展到(dào)使用這(zhè)些新的(de)API,其中新增的(de)指标可觀察方案的(de)支持。

庫測量記錄示例

    Meter meter = new Meter("io.opentelemetry.contrib.mongodb", "v1.0");
    Counter counter = meter.CreateCounter("Requests");
    counter.Add(1);
    counter.Add(1, KeyValuePair.Create("request", "read"));

聽力示例

    MeterListener listener = new MeterListener();
    listener.InstrumentPublished = (instrument, meterListener) =>
    {
        if (instrument.Name == "Requests" && instrument.Meter.Name == "io.opentelemetry.contrib.mongodb")
        {
            meterListener.EnableMeasurementEvents(instrument, null);
        }
    };
    listener.SetMeasurementEventCallback((instrument, measurement, tags, state) =>
    {
        Console.WriteLine($"Instrument: {instrument.Name} has recorded the measurement {measurement}");
    });
    listener.Start();

Windows 窗體

我們繼續在(zài) Windows 窗體中進行關鍵改進。.NET 6 包括更好的(de)控件可訪問性、設置應用程序範圍默認字體的(de)能力、模闆更新等。

輔助功能改進

在(zài)此版本中,我們增加了(le/liǎo)UIA提供商爲(wéi / wèi)CheckedListBox,LinkLabel,Panel,ScrollBar,TabControl和(hé / huò)TrackBar,使像旁白工具和(hé / huò)測試自動化與應用程序的(de)元素進行交互。

默認字體

現在(zài),您可以(yǐ)設置默認字體的(de)應用程序有。Application.SetDefaultFont

void Application.SetDefaultFont(Font font)

最少的(de)應用

以(yǐ)下是(shì)帶有 .NET 6的(de)最小 Windows 窗體應用程序

class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(new Form1());
    }
}

作爲(wéi / wèi) .NET 6 版本的(de)一(yī / yì /yí)部分,我們一(yī / yì /yí)直在(zài)更新大(dà)多數模闆,使其更加現代和(hé / huò)簡約,包括 Windows 窗體。我們決定讓 Windows 窗體模闆更傳統一(yī / yì /yí)些,部分原因是(shì)需要(yào / yāo)将該屬性應用于(yú)應用程序入口點。然而(ér),除了(le/liǎo)立即出(chū)現之(zhī)外,還有更多的(de)玩法。[STAThread]

ApplicationConfiguration.Initialize() 是(shì)一(yī / yì /yí)個(gè)源代碼生成的(de) API,它在(zài)後台發出(chū)以(yǐ)下調用:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetDefaultFont(new Font(...));
Application.SetHighDpiMode(HighDpiMode.SystemAware);

這(zhè)些調用的(de)參數可通過csproj 或 props 文件中的(de)MSBuild 屬性進行配置。

Visual Studio 2022 中的(de) Windows 窗體設計器也(yě)知道(dào)這(zhè)些屬性(目前它隻讀取默認字體),并且可以(yǐ)向您展示您的(de)應用程序,就(jiù)像它在(zài)運行時(shí)一(yī / yì /yí)樣:





模闆更新

C# 的(de) Windows 窗體模闆已更新,以(yǐ)支持新的(de)應用程序引導程序、指令、文件範圍的(de)命名空間和(hé / huò)可爲(wéi / wèi)空的(de)引用類型。global using

更多運行時(shí)設計器

現在(zài)您可以(yǐ)構建通用設計器(例如,報表設計器),因爲(wéi / wèi) .NET 6 具有設計器和(hé / huò)設計器相關基礎結構的(de)所有缺失部分。有關更多信息,請參閱此博客文章

單文件應用程序

在(zài) .NET 6 中,已爲(wéi / wèi) Windows 和(hé / huò) macOS 啓用内存中單文件應用程序。在(zài) .NET 5 中,這(zhè)種部署類型僅限于(yú) Linux。您現在(zài)可以(yǐ)爲(wéi / wèi)所有支持的(de)操作系統發布作爲(wéi / wèi)單個(gè)文件部署和(hé / huò)啓動的(de)單文件二進制文件。單文件應用程序不(bù)再将任何核心運行時(shí)程序集提取到(dào)臨時(shí)目錄。

這(zhè)種擴展功能基于(yú)稱爲(wéi / wèi)“超級主機”的(de)構建塊。“apphost”是(shì)在(zài)非單文件情況下啓動應用程序的(de)可執行文件,例如或。Apphost 包含用于(yú)查找運行時(shí)、加載它并使用該運行時(shí)啓動您的(de)應用程序的(de)代碼。Superhost 仍然執行其中一(yī / yì /yí)些任務,但使用所有 CoreCLR 本機二進制文件的(de)靜态鏈接副本。靜态鏈接是(shì)我們用來(lái)啓用單個(gè)文件體驗的(de)方法。myapp.exe./myapp

本機依賴項(如 NuGet 包附帶的(de)依賴項)是(shì)單文件嵌入的(de)顯着例外。默認情況下,它們不(bù)包含在(zài)單個(gè)文件中。例如,WPF 本機依賴項不(bù)是(shì)超級主機的(de)一(yī / yì /yí)部分,導緻除了(le/liǎo)單文件應用程序之(zhī)外還有其他(tā)文件。您可以(yǐ)使用該設置IncludeNativeLibrariesForSelfExtract來(lái)嵌入和(hé / huò)提取本機依賴項。

靜态分析

我們改進了(le/liǎo)單文件分析器以(yǐ)允許自定義警告。如果您的(de) API 在(zài)單文件發布中不(bù)起作用,您現在(zài)可以(yǐ)使用該屬性對其進行标記,如果啓用了(le/liǎo)分析器,則會出(chū)現警告。添加該屬性還将使方法中與單個(gè)文件相關的(de)所有警告靜音,因此您可以(yǐ)使用該警告将警告向上(shàng)傳播到(dào)您的(de)公共 API。[RequiresAssemblyFiles]

設置爲(wéi / wèi) 時(shí),會自動爲(wéi / wèi)exe項目啓用單文件分析器,但您也(yě)可以(yǐ)通過設置爲(wéi / wèi)爲(wéi / wèi)任何項目啓用它。如果您想支持庫作爲(wéi / wèi)單個(gè)文件應用程序的(de)一(yī / yì /yí)部分,這(zhè)會很有幫助。PublishSingleFiletrueEnableSingleFileAnalysistrue

在(zài) .NET 5 中,我們添加了(le/liǎo)警告和(hé / huò)其他(tā)一(yī / yì /yí)些在(zài)單文件包中表現不(bù)同的(de) API。Assembly.Location

壓縮

單文件包現在(zài)支持壓縮,可以(yǐ)通過将屬性設置EnableCompressionInSingleFile爲(wéi / wèi)true. 在(zài)運行時(shí),文件會根據需要(yào / yāo)解壓縮到(dào)内存中。壓縮可以(yǐ)爲(wéi / wèi)某些場景提供巨大(dà)的(de)空間節省。

讓我們看一(yī / yì /yí)下與NuGet 包資源管理器一(yī / yì /yí)起使用的(de)帶壓縮和(hé / huò)不(bù)帶壓縮的(de)單個(gè)文件發布。

無壓縮:172 MB





壓縮後:71.6 MB





壓縮可以(yǐ)顯着增加應用程序的(de)啓動時(shí)間,尤其是(shì)在(zài) Unix 平台上(shàng)。Unix 平台具有無法與壓縮一(yī / yì /yí)起使用的(de)無複制快速啓動路徑。您應該在(zài)啓用壓縮後測試您的(de)應用,看看額外的(de)啓動成本是(shì)否可以(yǐ)接受。

單文件調試

單文件應用程序目前隻能使用平台調試器(如 WinDBG)進行調試。我們正在(zài)考慮在(zài) Visual Studio 2022 的(de)更高版本中添加 Visual Studio 調試。

macOS 上(shàng)的(de)單文件簽名

單文件應用程序現在(zài)滿足 macOS 上(shàng)的(de) Apple 公證和(hé / huò)簽名要(yào / yāo)求。在(zài)具體的(de)變化涉及到(dào)我們在(zài)離散文件布局方面構建單個(gè)文件的(de)應用程序的(de)方式。

蘋果開始執行新規定的(de)簽署和(hé / huò)公證MacOS的(de)卡特琳娜。我們一(yī / yì /yí)直在(zài)與 Apple 密切合作,以(yǐ)了(le/liǎo)解要(yào / yāo)求,并尋找解決方案,使 .NET 等開發平台能夠在(zài)該環境中良好運行。在(zài)最近的(de)幾個(gè) .NET 版本中,我們已經對産品進行了(le/liǎo)更改并記錄了(le/liǎo)用戶工作流程,以(yǐ)滿足 Apple 的(de)要(yào / yāo)求。剩下的(de)差距之(zhī)一(yī / yì /yí)是(shì)單文件簽名,這(zhè)是(shì)在(zài) macOS 上(shàng)分發 .NET 應用程序的(de)要(yào / yāo)求,包括在(zài) macOS 商店中。

IL 修整

該團隊一(yī / yì /yí)直緻力于(yú)爲(wéi / wèi)多個(gè)版本進行 IL 修整。.NET 6 代表了(le/liǎo)這(zhè)一(yī / yì /yí)旅程的(de)重要(yào / yāo)一(yī / yì /yí)步。我們一(yī / yì /yí)直在(zài)努力使更激進的(de)修剪模式安全且可預測,因此有信心将其設爲(wéi / wèi)默認值。以(yǐ)前是(shì)選擇加入功能,現在(zài)是(shì)默認功能。TrimMode=link

我們有一(yī / yì /yí)個(gè)三管齊下的(de)修剪策略:

  • 提高平台的(de)修剪能力。

  • 對平台進行注釋以(yǐ)提供更好的(de)警告并使其他(tā)人(rén)也(yě)能這(zhè)樣做。

  • 在(zài)此基礎上(shàng),使默認修剪模式更加激進,以(yǐ)便輕松将應用程序變小。

由于(yú)使用未注釋反射的(de)應用程序的(de)結果不(bù)可靠,因此修剪之(zhī)前一(yī / yì /yí)直處于(yú)預覽狀态。有了(le/liǎo)修剪警告,體驗現在(zài)應該是(shì)可預測的(de)。沒有修剪警告的(de)應用程序應該正确修剪并且在(zài)運行時(shí)觀察到(dào)行爲(wéi / wèi)沒有變化。目前,隻有核心 .NET 庫已經完全注釋用于(yú)修剪,但我們希望看到(dào)生态系統注釋用于(yú)修剪并成爲(wéi / wèi)修剪兼容

減少應用程序大(dà)小

讓我們來(lái)看看使用crossgen 的(de)這(zhè)種修剪改進,它是(shì) SDK 工具之(zhī)一(yī / yì /yí)。可以(yǐ)隻用一(yī / yì /yí)些修剪警告來(lái)修剪它,crossgen 團隊能夠解決這(zhè)些問題。

首先,讓我們将 crossgen 發布爲(wéi / wèi)一(yī / yì /yí)個(gè)獨立的(de)應用程序,無需修剪。它是(shì) 80 MB(包括 .NET 運行時(shí)和(hé / huò)所有庫)。





然後我們可以(yǐ)嘗試(現在(zài)是(shì)舊版).NET 5 默認修剪模式,copyused. 結果下降到(dào) 55 MB。





新的(de) .NET 6 默認修剪模式link将自包含文件大(dà)小進一(yī / yì /yí)步降低到(dào) 36MB。





我們希望新的(de)link修剪模式能更好地(dì / de)滿足修剪的(de)期望:顯着的(de)節省和(hé / huò)可預測的(de)結果。

默認啓用警告

修剪警告告訴您修剪可能會删除運行時(shí)使用的(de)代碼的(de)地(dì / de)方。這(zhè)些警告以(yǐ)前在(zài)默認情況下被禁用,因爲(wéi / wèi)警告非常嘈雜,主要(yào / yāo)是(shì)由于(yú) .NET 平台沒有作爲(wéi / wèi)第一(yī / yì /yí)類場景參與修剪。

我們對 .NET 庫的(de)大(dà)部分進行了(le/liǎo)注釋,以(yǐ)便它們生成準确的(de)修剪警告。因此,我們認爲(wéi / wèi)是(shì)時(shí)候默認啓用修剪警告了(le/liǎo)。http://ASP.NETCore 和(hé / huò) Windows 桌面運行時(shí)庫尚未注釋。我們計劃接下來(lái)(.NET 6 之(zhī)後)注釋http://ASP.NET服務組件。我們希望看到(dào)社區在(zài) .NET 6 發布後對 NuGet 庫進行注釋。

您可以(yǐ)通過設置爲(wéi / wèi)來(lái)禁用警告true。

更多信息:

與本機 AOT 共享

我們也(yě)爲(wéi / wèi)Native AOT 實驗實施了(le/liǎo)相同的(de)修剪警告,這(zhè)應該會以(yǐ)大(dà)緻相同的(de)方式改善 Native AOT 編譯體驗。

數學

我們顯着改進了(le/liǎo)數學 API。社區的(de)一(yī / yì /yí)些人(rén)已經在(zài)享受這(zhè)些改進

面向性能的(de) API

面向性能的(de)數學 API 已添加到(dào) System.Math。如果底層硬件支持,它們的(de)實現是(shì)硬件加速的(de)。

新 API:

  • SinCos用于(yú)同時(shí)計算Sin和(hé / huò)Cos。

  • ReciprocalEstimate用于(yú)計算 的(de)近似值。1 / x

  • ReciprocalSqrtEstimate用于(yú)計算 的(de)近似值。1 / Sqrt(x)

新的(de)重載:

  • Clamp、DivRem、Min、 和(hé / huò)Max支持nint和(hé / huò)nuint。

  • Abs和(hé / huò)Sign支持nint。

  • DivRem返回 a 的(de)變體tuple。

性能改進:

大(dà)整數性能

從十進制和(hé / huò)十六進制字符串解析 BigIntegers已得到(dào)改進。我們看到(dào)了(le/liǎo)高達 89% 的(de)改進,如下圖所示(越低越好)。





歸功于(yú)約瑟夫·達席爾瓦

Complex API 現在(zài)注釋爲(wéi / wèi) readonly

href="https://github.com/dotnet/runtime/pull/51797/">現在(zài)System.Numerics.Complexreadonly對各種API 進行了(le/liǎo)注釋,以(yǐ)确保不(bù)會爲(wéi / wèi)readonly通過in.

感謝hrrrrustic

BitConverter 現在(zài)支持浮點到(dào)無符号整數比特轉換

BitConverter ref="https://github.com/dotnet/runtime/pull/53784">現在(zài)支持DoubleToUInt64Bits,HalfToUInt16Bits,SingleToUInt32Bits,UInt16BitsToHalf,UInt32BitsToSingle,和(hé / huò)UInt64BitsToDouble。這(zhè)應該可以(yǐ)在(zài)需要(yào / yāo)時(shí)更容易地(dì / de)進行浮點位操作。

感謝米哈爾Petryka

BitOperations 支持附加功能

BitOperations現在(zài)支持IsPow2RoundUpToPowerOf2爲(wéi / wèi)現有函數href="https://github.com/dotnet/runtime/pull/58733">提供nint/nuint重載。

感謝約翰·凱利霍耀元和(hé / huò)羅賓·林德納

Vector, Vector2, Vector3, 和(hé / huò)Vector4改進

Vectorref="https://github.com/dotnet/runtime/pull/50832">現在(zài)支持nintnuintC# 9 中添加的(de)和(hé / huò)原始類型。例如,此更改應該可以(yǐ)更輕松地(dì / de)使用帶有指針或平台相關長度類型的(de) SIMD 指令。

Vectorf="https://github.com/dotnet/runtime/pull/53527">現在(zài)支持一(yī / yì /yí)種Sum方法來(lái)簡化需要(yào / yāo)計算向量中所有元素的(de)“水平總和(hé / huò)”。感謝伊萬茲拉塔諾夫

Vector"https://githhttp://ub.com/dotnet/runtime/pull/47150">現在(zài)支持通用方法As來(lái)簡化在(zài)具體類型未知的(de)通用上(shàng)下文中處理向量。感謝霍耀元

ref="https://github.com/dotnet/runtime/pull/50062">重載支持Span已添加到(dào)Vector2、Vector3和(hé / huò)Vector4以(yǐ)改善需要(yào / yāo)加載或存儲向量類型時(shí)的(de)體驗。

更好地(dì / de)解析标準數字格式

我們改進了(le/liǎo)标準數字類型的(de)解析器,特别是(shì)和(hé / huò)。他(tā)們現在(zài)将理解精度要(yào / yāo)求 > 99 位小數,并将提供精确到(dào)那麽多位數的(de)結果。此外,解析器現在(zài)更好地(dì / de)支持方法中的(de)尾随零。.ToString.TryFormatParse

以(yǐ)下示例演示了(le/liǎo)前後行爲(wéi / wèi)。

  • 32.ToString("C100") -> C132

    • .NET 6: $32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

    • .NET 5:我們在(zài)格式化代碼中存在(zài)人(rén)爲(wéi / wèi)限制,隻能處理 <= 99="">= 100,我們将輸入解釋爲(wéi / wèi)自定義格式。


  • 32.ToString("H99") -> 扔一(yī / yì /yí)個(gè) FormatException

    • .NET 6:抛出(chū) FormatException

    • 這(zhè)是(shì)正确的(de)行爲(wéi / wèi),但在(zài)此處調用它是(shì)爲(wéi / wèi)了(le/liǎo)與下一(yī / yì /yí)個(gè)示例進行對比。


  • 32.ToString("H100") -> H132

    • .NET 6:抛出(chū) FormatException

    • .NET 5:H是(shì)無效的(de)格式說(shuō)明符。所以(yǐ),我們應該抛出(chū)一(yī / yì /yí)個(gè)FormatException. 相反,我們将精度 >= 100 解釋爲(wéi / wèi)自定義格式的(de)錯誤行爲(wéi / wèi)意味着我們返回了(le/liǎo)錯誤的(de)值。


  • double.Parse("9007199254740997.0") -> 9007199254740998

    • .NET 6: 9007199254740996.

    • .NET 5:9007199254740997.0不(bù)能完全以(yǐ) IEEE 754 格式表示。使用我們當前的(de)舍入方案,正确的(de)返回值應該是(shì)9007199254740996。但是(shì),輸入的(de)最後一(yī / yì /yí)部分迫使解析器錯誤地(dì / de)舍入結果并返回。.09007199254740998


系統.文本.Json

System.Text.Json提供了(le/liǎo)多種高性能的(de) API 來(lái)處理 JSON 文檔。在(zài)過去的(de)幾個(gè)版本中,我們添加了(le/liǎo)新功能,以(yǐ)進一(yī / yì /yí)步提高 JSON 處理性能并減輕想要(yào / yāo)從. 此版本包括在(zài)這(zhè)條道(dào)路上(shàng)的(de)繼續,并且是(shì)性能的(de)重大(dà)進步,特别是(shì)在(zài)序列化器源生成器方面。NewtonSoft.Json

JsonSerializer 源代碼生成

注意:應重新編譯使用 .NET 6 RC1 或更早版本的(de)源代碼生成的(de)應用程序。

幾乎所有 .NET 序列化程序的(de)支柱都是(shì)反射。反射對于(yú)某些場景來(lái)說(shuō)是(shì)一(yī / yì /yí)項很棒的(de)功能,但不(bù)能作爲(wéi / wèi)高性能雲原生應用程序(通常(反)序列化和(hé / huò)處理大(dà)量 JSON 文檔)的(de)基礎。反射是(shì)啓動、内存使用和(hé / huò)程序集修整的(de)問題

運行時(shí)反射的(de)替代方案是(shì)編譯時(shí)源代碼生成。在(zài) .NET 6 中,我們将一(yī / yì /yí)個(gè)"https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/">新的(de)源生成器作爲(wéi / wèi)System.Text.Json. JSON 源生成器可與JsonSerializer多種方式結合使用并可進行配置。

它可以(yǐ)提供以(yǐ)下好處:

  • 減少啓動時(shí)間

  • 提高序列化吞吐量

  • 減少私有内存使用

  • 删除運行時(shí)使用和(hé / huò)System.ReflectionSystem.Reflection.Emit

  • IL 修整兼容性

默認情況下,JSON 源生成器爲(wéi / wèi)給定的(de)可序列化類型發出(chū)序列化邏輯。JsonSerializer通過生成Utf8JsonWriter直接使用的(de)源代碼,這(zhè)提供了(le/liǎo)比使用現有方法更高的(de)性能。簡而(ér)言之(zhī),源代碼生成器提供了(le/liǎo)一(yī / yì /yí)種在(zài)編譯時(shí)爲(wéi / wèi)您提供不(bù)同實現的(de)方法,以(yǐ)便使運行時(shí)體驗更好。

給定一(yī / yì /yí)個(gè)簡單類型:

namespace Test
{
    internal class JsonMessage
    {
        public string Message { get; set; }
    }
}

源生成器可以(yǐ)配置爲(wéi / wèi)爲(wéi / wèi)示例JsonMessage類型的(de)實例生成序列化邏輯。請注意,類名JsonContext是(shì)任意的(de)。您可以(yǐ)爲(wéi / wèi)生成的(de)源使用任何您想要(yào / yāo)的(de)類名。

using System.Text.Json.Serialization;

namespace Test
{
    [JsonSerializable(typeof(JsonMessage)]
    internal partial class JsonContext : JsonSerializerContext
    {
    }
}

使用此模式的(de)序列化程序調用可能類似于(yú)以(yǐ)下示例。此示例提供了(le/liǎo)可能的(de)最佳性能。

using MemoryStream ms = new();
using Utf8JsonWriter writer = new(ms);

JsonSerializer.Serialize(jsonMessage, JsonContext.Default.JsonMessage);
writer.Flush();

// Writer contains:
// {"Message":"Hello, world!"}

最快和(hé / huò)最優化的(de)源代碼生成模式——基于(yú)Utf8JsonWriter——目前僅可用于(yú)序列化。Utf8JsonReader根據您的(de)反饋,未來(lái)可能會提供類似的(de)反序列化支持——基于(yú)——。

源生成器還發出(chū)類型元數據初始化邏輯,這(zhè)也(yě)有利于(yú)反序列化。要(yào / yāo)反序列化JsonMessage使用預生成類型元數據的(de)實例,您可以(yǐ)執行以(yǐ)下操作:

JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);

JsonSerializer 支持 IAsyncEnumerable

現在(zài),可以(yǐ)(反)序列化JSON陣列IAsyncEnumerable與。以(yǐ)下示例使用流作爲(wéi / wèi)數據的(de)任何異步源的(de)表示。源可以(yǐ)是(shì)本地(dì / de)機器上(shàng)的(de)文件,也(yě)可以(yǐ)是(shì)數據庫查詢或 Web 服務 API 調用的(de)結果。System.Text.Json

JsonSerializer.SerializeAsync已更新以(yǐ)識别IAsyncEnumerable值并提供特殊處理。

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;

static async IAsyncEnumerable PrintNumbers(int n)
{
    for (int i = 0; i < n; i++) yield return i;
}

using Stream stream = Console.OpenStandardOutput();
var data = new { Data = PrintNumbers(3) };
await JsonSerializer.SerializeAsync(stream, data); // prints {"Data":[0,1,2]}

IAsyncEnumerable值僅支持使用異步序列化方法。嘗試使用同步方法進行序列化将導緻NotSupportedException抛出(chū)異常。

流式反序列化需要(yào / yāo)一(yī / yì /yí)個(gè)新的(de) API 返回. 我們爲(wéi / wèi)此目的(de)添加了(le/liǎo)該方法,您可以(yǐ)在(zài)以(yǐ)下示例中看到(dào)。IAsyncEnumerableJsonSerializer.DeserializeAsyncEnumerable

using System;
using System.IO;
using System.Text;
using System.Text.Json;

var stream = new MemoryStream(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));
await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable(stream))
{
    Console.WriteLine(item);
}

此示例将按需反序列化元素,并且在(zài)使用特别大(dà)的(de)數據流時(shí)非常有用。它隻支持從根級 JSON 數組中讀取,盡管将來(lái)可能會根據反饋放寬。

現有DeserializeAsync方法名義上(shàng)支持,但在(zài)其非流式方法簽名的(de)範圍内。它必須将最終結果作爲(wéi / wèi)單個(gè)值返回,如下例所示。IAsyncEnumerable

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;

var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"{""Data"":[0,1,2,3,4]}"));
var result = await JsonSerializer.DeserializeAsync(stream);
await foreach (int item in result.Data)
{
    Console.WriteLine(item);
}

public class MyPoco
{
    public IAsyncEnumerable Data { get; set; }
}

在(zài)此示例中,反序列化器将IAsyncEnumerable在(zài)返回反序列化對象之(zhī)前緩沖内存中的(de)所有内容。這(zhè)是(shì)因爲(wéi / wèi)反序列化器需要(yào / yāo)在(zài)返回結果之(zhī)前消耗整個(gè) JSON 值。

System.Text.Json:可寫 DOM 功能

在(zài)寫JSON DOM功能增加了(le/liǎo)一(yī / yì /yí)個(gè)新的(de)簡單的(de),高性能的(de)編程模型的(de)。這(zhè)個(gè)新的(de) API 很有吸引力,因爲(wéi / wèi)它避免了(le/liǎo)需要(yào / yāo)強類型的(de)序列化契約,并且與現有類型相比,DOM 是(shì)可變的(de)。System.Text.JsonJsonDocument

這(zhè)個(gè)新的(de) API 有以(yǐ)下好處:

  • 在(zài)不(bù)可能或不(bù)希望使用POCO類型的(de)情況下,或者當 JSON 模式不(bù)固定且必須檢查時(shí),序列化的(de)輕量級替代方案。

  • 允許對大(dà)樹的(de)子(zǐ)集進行有效修改。例如,可以(yǐ)高效地(dì / de)導航到(dào)大(dà)型 JSON 樹的(de)子(zǐ)部分并從該子(zǐ)部分讀取數組或反序列化 POCO。LINQ 也(yě)可以(yǐ)與它一(yī / yì /yí)起使用。

以(yǐ)下示例演示了(le/liǎo)新的(de)編程模型。

    // Parse a JSON object
    JsonNode jNode = JsonNode.Parse("{"MyProperty":42}");
    int value = (int)jNode["MyProperty"];
    Debug.Assert(value == 42);
    // or
    value = jNode["MyProperty"].GetValue();
    Debug.Assert(value == 42);

    // Parse a JSON array
    jNode = JsonNode.Parse("[10,11,12]");
    value = (int)jNode[1];
    Debug.Assert(value == 11);
    // or
    value = jNode[1].GetValue();
    Debug.Assert(value == 11);

    // Create a new JsonObject using object initializers and array params
    var jObject = new JsonObject
    {
        ["MyChildObject"] = new JsonObject
        {
            ["MyProperty"] = "Hello",
            ["MyArray"] = new JsonArray(10, 11, 12)
        }
    };

    // Obtain the JSON from the new JsonObject
    string json = jObject.ToJsonString();
    Console.WriteLine(json); // {"MyChildObject":{"MyProperty":"Hello","MyArray":[10,11,12]}}

    // Indexers for property names and array elements are supported and can be chained
    Debug.Assert(jObject["MyChildObject"]["MyArray"][1].GetValue() == 11);

ReferenceHandler.IgnoreCycles

JsonSerializer(System.Text.Json)現在(zài)支持在(zài)序列化對象圖時(shí)忽略循環的(de)能力。該選項的(de)行爲(wéi / wèi)與 Newtonsoft.Json 相似。一(yī / yì /yí)個(gè)主要(yào / yāo)區别是(shì) System.Text.Json 實現用JSON 标記替換引用循環,而(ér)不(bù)是(shì)忽略對象引用。ReferenceHandler.IgnoreCyclesReferenceLoopHandling.Ignorenull

您可以(yǐ)在(zài)以(yǐ)下示例中看到(dào) 的(de)行爲(wéi / wèi)。在(zài)這(zhè)種情況下,屬性被序列化,因爲(wéi / wèi)它否則會創建一(yī / yì /yí)個(gè)循環。ReferenceHandler.IgnoreCyclesNextnull

class Node
{
    public string Description { get; set; }
    public object Next { get; set; }
}

void Test()
{
    var node = new Node { Description = "Node 1" };
    node.Next = node;

    var opts = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles };

    string json = JsonSerializer.Serialize(node, opts);
    Console.WriteLine(json); // Prints {"Description":"Node 1","Next":null}
}

源代碼構建

使用源代碼構建,隻需使用幾條命令即可在(zài)您自己的(de)機器上(shàng)從源代碼構建 .NET SDK。讓我解釋一(yī / yì /yí)下爲(wéi / wèi)什麽這(zhè)個(gè)項目很重要(yào / yāo)。

自 .NET Core 1.0 發布之(zhī)前,源代碼構建是(shì)一(yī / yì /yí)個(gè)場景,也(yě)是(shì)我們與 Red Hat 合作開發的(de)基礎設施。幾年後,我們非常接近提供它的(de)完全自動化版本。對于(yú) Red Hat Enterprise Linux (RHEL) .NET 用戶來(lái)說(shuō),此功能非常重要(yào / yāo)。紅帽告訴我們,.NET 已經發展成爲(wéi / wèi)其生态系統的(de)重要(yào / yāo)開發者平台。好的(de)!

Linux 發行版的(de)黃金标準是(shì)使用作爲(wéi / wèi)發行版存檔一(yī / yì /yí)部分的(de)編譯器和(hé / huò)工具鏈構建開源代碼。這(zhè)适用于(yú) .NET 運行時(shí)(用 C++ 編寫),但不(bù)适用于(yú)任何用 C# 編寫的(de)代碼。對于(yú) C# 代碼,我們使用兩遍構建機制來(lái)滿足發行版要(yào / yāo)求。這(zhè)有點複雜,但了(le/liǎo)解流程很重要(yào / yāo)。

Red Hat 使用 .NET SDK (#1) 的(de) Microsoft 二進制構建來(lái)構建 .NET SDK 源代碼,以(yǐ)生成 SDK (#2) 的(de)純開源二進制構建。之(zhī)後,使用 SDK 的(de)新版本 (#2) 再次構建相同的(de) SDK 源代碼,以(yǐ)生成可證明的(de)開源 SDK (#3)。.NET SDK (#3) 的(de)最終二進制版本随後可供 RHEL 用戶使用。之(zhī)後,Red Hat 可以(yǐ)使用相同的(de) SDK (#3) 來(lái)構建新的(de) .NET 版本,而(ér)不(bù)再需要(yào / yāo)使用 Microsoft SDK 來(lái)構建月度更新。

這(zhè)個(gè)過程可能令人(rén)驚訝和(hé / huò)困惑。開源發行版需要(yào / yāo)由開源工具構建。此模式确保不(bù)需要(yào / yāo) Microsoft 構建的(de) SDK,無論是(shì)有意還是(shì)無意。作爲(wéi / wèi)開發者平台,被包含在(zài)發行版中比僅使用兼容許可證有更高的(de)标準。源構建項目使 .NET 能夠滿足這(zhè)一(yī / yì /yí)要(yào / yāo)求。

源代碼構建的(de)交付物是(shì)一(yī / yì /yí)個(gè)源代碼壓縮包。源 tarball 包含 SDK(對于(yú)給定版本)的(de)所有源。從那裏,紅帽(或其他(tā)組織)可以(yǐ)構建自己的(de) SDK 版本。Red Hat 策略需要(yào / yāo)使用從源代碼構建的(de)工具鏈來(lái)生成二進制 tar 球,這(zhè)就(jiù)是(shì)他(tā)們使用兩遍方法的(de)原因。但是(shì)源代碼構建本身不(bù)需要(yào / yāo)這(zhè)種兩遍方法。

在(zài) Linux 生态系統中,爲(wéi / wèi)給定組件提供源代碼和(hé / huò)二進制包或 tarball 是(shì)很常見的(de)。我們已經有可用的(de)二進制 tarball,現在(zài)也(yě)有源 tarball。這(zhè)使得 .NET 與标準組件模式相匹配。

.NET 6 的(de)重大(dà)改進是(shì)源 tarball 現在(zài)是(shì)我們構建的(de)産品。過去,它需要(yào / yāo)大(dà)量的(de)人(rén)工來(lái)制作,這(zhè)也(yě)導緻将源 tarball 傳送到(dào) Red Hat 的(de)延遲很長。雙方對此都不(bù)滿意。

五年多以(yǐ)來(lái),我們一(yī / yì /yí)直與紅帽在(zài)這(zhè)個(gè)項目上(shàng)密切合作。它取得了(le/liǎo)成功,在(zài)很大(dà)程度上(shàng)要(yào / yāo)歸功于(yú)我們有幸與之(zhī)共事的(de)優秀紅帽工程師的(de)努力。其他(tā)發行版和(hé / huò)組織已經并将從他(tā)們的(de)努力中受益。

附帶說(shuō)明一(yī / yì /yí)下,源代碼構建是(shì)朝着可重現構建邁出(chū)的(de)一(yī / yì /yí)大(dà)步,我們也(yě)堅信這(zhè)一(yī / yì /yí)點。.NET SDK 和(hé / huò) C# 編譯器具有重要(yào / yāo)的(de)可重現構建功能。

庫 API

除了(le/liǎo)已經涵蓋的(de) API 之(zhī)外,還添加了(le/liǎo)以(yǐ)下 API。

WebSocket 壓縮

壓縮對于(yú)通過網絡傳輸的(de)任何數據都很重要(yào / yāo)。WebSockets 現在(zài)啓用壓縮。我們使用了(le/liǎo)WebSockets的(de)擴展實現,RFC 7692。它允許使用該算法壓縮 WebSockets 消息有效負載。此功能是(shì)用戶對 GitHub 上(shàng) Networking 的(de)最高要(yào / yāo)求之(zhī)一(yī / yì /yí)。permessage-deflateDEFLATE

與加密一(yī / yì /yí)起使用的(de)壓縮可能會導緻攻擊,例如CRIME和(hé / huò)BREACH。這(zhè)意味着不(bù)能在(zài)單個(gè)壓縮上(shàng)下文中将秘密與用戶生成的(de)數據一(yī / yì /yí)起發送,否則可以(yǐ)提取該秘密。爲(wéi / wèi)了(le/liǎo)讓用戶注意這(zhè)些影響并幫助他(tā)們權衡風險,我們将其中一(yī / yì /yí)個(gè)關鍵 API 命名爲(wéi / wèi)DangerousDeflateOptions。我們還添加了(le/liǎo)對特定消息關閉壓縮的(de)功能,因此如果用戶想要(yào / yāo)發送機密,他(tā)們可以(yǐ)在(zài)不(bù)壓縮的(de)情況下安全地(dì / de)發送。

禁用壓縮時(shí) WebSocket的(de)内存占用減少了(le/liǎo)約 27%。

從客戶端啓用壓縮很容易,如下例所示。但是(shì),請記住,服務器可以(yǐ)協商設置,例如請求較小的(de)窗口或完全拒絕壓縮。

var cws = new ClientWebSocket();
cws.Options.DangerousDeflateOptions = new WebSocketDeflateOptions()
{
    ClientMaxWindowBits = 10,
    ServerMaxWindowBits = 10
};

還添加了(le/liǎo)對 ASP.NET Core 的(de) WebSocket 壓縮支持

感謝伊萬茲拉塔諾夫

襪子(zǐ)代理支持

SOCKS是(shì)一(yī / yì /yí)種代理服務器實現,可以(yǐ)處理任何 TCP 或 UDP 流量,使其成爲(wéi / wèi)一(yī / yì /yí)個(gè)非常通用的(de)系統。這(zhè)是(shì)一(yī / yì /yí)個(gè)長期存在(zài)的(de)社區請求,已添加到(dào) .NET 6 中

此更改增加了(le/liǎo)對 Socks4、Socks4a 和(hé / huò) Socks5 的(de)支持。例如,它允許通過 SSH 測試外部連接或連接到(dào) Tor 網絡

該WebProxy班現在(zài)接受socks的(de)方案,你可以(yǐ)在(zài)下面的(de)例子(zǐ)中看到(dào)。

var handler = new HttpClientHandler
{
    Proxy = new WebProxy("socks5://127.0.0.1", 9050)
};
var httpClient = new HttpClient(handler);

感謝霍耀元

Microsoft.Extensions.Hosting — ConfigureHostOptions API

我們在(zài) IHostBuilder 上(shàng)添加了(le/liǎo)一(yī / yì /yí)個(gè)新的(de) ConfigureHostOptions API 以(yǐ)簡化應用程序設置(例如,配置關閉超時(shí)):

using HostBuilder host = new()
    .ConfigureHostOptions(o =>
    {
        o.ShutdownTimeout = TimeSpan.FromMinutes(10);
    })
    .Build();

host.Run();

在(zài) .NET 5 中,配置主機選項有點複雜:

using HostBuilder host = new()
    .ConfigureServices(services =>
    {
        services.Configure(o =>
        {
            o.ShutdownTimeout = TimeSpan.FromMinutes(10);
        });
    })
    .Build();

host.Run();

Microsoft.Extensions.DependencyInjection — CreateAsyncScope API

CreateAsyncScopeAPI是(shì)爲(wéi / wèi)了(le/liǎo)處理處置IAsyncDisposable服務。以(yǐ)前,您可能已經注意到(dào),對IAsyncDisposable服務提供者的(de)處置可能會引發InvalidOperationException異常。

下面的(de)示例演示了(le/liǎo)新模式,CreateAsyncScope用于(yú)啓用using語句的(de)安全使用。

await using (var scope = provider.CreateAsyncScope())
{
    var foo = scope.ServiceProvider.GetRequiredService();
}

下面的(de)例子(zǐ)演示了(le/liǎo)現有的(de)問題案例:

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

await using var provider = new ServiceCollection()
        .AddScoped()
        .BuildServiceProvider();

// This using can throw InvalidOperationException
using (var scope = provider.CreateScope())
{
    var foo = scope.ServiceProvider.GetRequiredService();
}

class Foo : IAsyncDisposable
{
    public ValueTask DisposeAsync() => default;
}

以(yǐ)下模式是(shì)之(zhī)前建議的(de)避免異常的(de)解決方法。不(bù)再需要(yào / yāo)它。

var scope = provider.CreateScope();
var foo = scope.ServiceProvider.GetRequiredService();
await ((IAsyncDisposable)scope).DisposeAsync();

感謝馬丁Björkström

Microsoft.Extensions.Logging — 編譯時(shí)源代碼生成器

.NET 6href="https://github.com/dotnet/runtime/issues/52549">引入了(le/liǎo)LoggerMessageAttribute類型. 此屬性是(shì)命名空間的(de)一(yī / yì /yí)部分,使用時(shí),它會源生成高性能日志 API。源代碼生成日志支持旨在(zài)爲(wéi / wèi)現代 .NET 應用程序提供高度可用和(hé / huò)高性能的(de)日志解決方案。自動生成的(de)源代碼依賴于(yú)接口和(hé / huò)功能。Microsoft.Extensions.LoggingILoggerLoggerMessage.Define

當LoggerMessageAttribute用于(yú)partial日志記錄方法時(shí)觸發源生成器。當被觸發時(shí),它要(yào / yāo)麽能夠自動生成partial它正在(zài)裝飾的(de)方法的(de)實現,要(yào / yāo)麽生成帶有正确使用提示的(de)編譯時(shí)診斷。編譯時(shí)日志記錄解決方案在(zài)運行時(shí)通常比現有日志記錄方法快得多。它通過最大(dà)限度地(dì / de)消除裝箱、臨時(shí)分配和(hé / huò)副本來(lái)實現這(zhè)一(yī / yì /yí)點。

與直接手動使用API 相比,有以(yǐ)下好處:LoggerMessage.Define

  • 更短更簡單的(de)語法:聲明性屬性使用而(ér)不(bù)是(shì)編碼樣闆。

  • 引導開發者體驗:生成器給出(chū)警告,幫助開發者做正确的(de)事。

  • 支持任意數量的(de)日志參數。最多支持六個(gè)。LoggerMessage.Define

  • 支持動态日志級别。這(zhè)是(shì)不(bù)可能的(de)。LoggerMessage.Define

要(yào / yāo)使用LoggerMessageAttribute,消費類和(hé / huò)方法需要(yào / yāo)是(shì)partial。代碼生成器在(zài)編譯時(shí)觸發并生成該partial方法的(de)實現。

public static partial class Log
{
    [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")]
    public static partial void CouldNotOpenSocket(ILogger logger, string hostName);
}

在(zài)前面的(de)示例中,日志記錄方法是(shì),static并且在(zài)屬性定義中指定了(le/liǎo)日志級别。在(zài)靜态上(shàng)下文中使用屬性時(shí),ILogger需要(yào / yāo)實例作爲(wéi / wèi)參數。您也(yě)可以(yǐ)選擇在(zài)非靜态上(shàng)下文中使用該屬性。有關更多示例和(hé / huò)使用場景,請訪問編譯時(shí)日志源生成器文檔。

System.Linq — 可枚舉支持Index和(hé / huò)Range參數

該方法現在(zài)接受可枚舉項末尾的(de)索引,如下例所示。Enumerable.ElementAt

Enumerable.Range(1, 10).ElementAt(^2); // returns 9

添加了(le/liǎo)一(yī / yì /yí)個(gè)接受參數的(de)重載。它簡化了(le/liǎo)對可枚舉序列的(de)切片:Enumerable.TakeRange

  • source.Take(..3) 代替 source.Take(3)

  • source.Take(3..) 代替 source.Skip(3)

  • source.Take(2..7) 代替 source.Take(7).Skip(2)

  • source.Take(^3..) 代替 source.TakeLast(3)

  • source.Take(..^3) 代替 source.SkipLast(3)

  • source.Take(^7..^3)而(ér)不(bù)是(shì).source.TakeLast(7).SkipLast(3)

感謝@dixin

System.Linq — TryGetNonEnumeratedCount

該TryGetNonEnumeratedCount方法嘗試在(zài)不(bù)強制枚舉的(de)情況下獲取源可枚舉的(de)計數。這(zhè)種方法在(zài)枚舉之(zhī)前預分配緩沖區很有用的(de)情況下很有用,如下例所示。

List buffer = source.TryGetNonEnumeratedCount(out int count) ? new List(capacity: count) : new List();
foreach (T item in source)
{
    buffer.Add(item);
}

TryGetNonEnumeratedCount檢查實現ICollection/或利用Linq 使用的(de)一(yī / yì /yí)些内部優化的(de)源。ICollection

System.Linq — DistinctBy/ UnionBy/ IntersectBy/ExceptBy

新的(de)變體已添加到(dào)集合操作中,允許使用鍵選擇器函數指定相等性,如下面的(de)示例所示。

Enumerable.Range(1, 20).DistinctBy(x => x % 3); // {1, 2, 3}

var first = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };
var second = new (string Name, int Age)[] { ("Claire", 30), ("Pat", 30), ("Drew", 33) };
first.UnionBy(second, person => person.Age); // { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40), ("Drew", 33) }

System.Linq — MaxBy/MinBy

MaxBy和(hé / huò)MinBy方法允許使用鍵選擇器查找最大(dà)或最小元素,如下例所示。

var people = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };
people.MaxBy(person => person.Age); // ("Ashley", 40)

System.Linq — Chunk

Chunk 可用于(yú)将可枚舉的(de)源分塊爲(wéi / wèi)固定大(dà)小的(de)切片,如下例所示。

IEnumerable chunks = Enumerable.Range(0, 10).Chunk(size: 3); // { {0,1,2}, {3,4,5}, {6,7,8}, {9} }

歸功于(yú)羅伯特·安德森

System.Linq的(de)- FirstOrDefault/ LastOrDefault/SingleOrDefault過載采取默認參數

現有FirstOrDefault/ LastOrDefault/SingleOrDefault方法返回如果源枚舉是(shì)空的(de)。添加了(le/liǎo)新的(de)重載,接受在(zài)這(zhè)種情況下要(yào / yāo)返回的(de)默認參數,如下面的(de)示例所示。default(T)

Enumerable.Empty().SingleOrDefault(-1); // returns -1

感謝@ Foxtrek64

System.Linq —Zip接受三個(gè)枚舉的(de)重載

郵編方法現在(zài)支持組合三個(gè)枚舉接口,你可以(yǐ)在(zài)下面的(de)例子(zǐ)中看到(dào)。

var xs = Enumerable.Range(1, 10);
var ys = xs.Select(x => x.ToString());
var zs = xs.Select(x => x % 2 == 0);

foreach ((int x, string y, bool z) in Enumerable.Zip(xs,ys,zs))
{
}

感謝霍耀元

優先隊列

PriorityQueue(System.Collections.Generic) 是(shì)一(yī / yì /yí)個(gè)新集合,可以(yǐ)添加具有值和(hé / huò)優先級的(de)新項目。在(zài)出(chū)隊時(shí),PriorityQueue 返回具有最低優先級值的(de)元素。您可以(yǐ)将這(zhè)個(gè)新集合視爲(wéi / wèi)類似于(yú)但每個(gè)入隊元素都有一(yī / yì /yí)個(gè)影響出(chū)隊行爲(wéi / wèi)的(de)優先級值。Queue

以(yǐ)下示例演示了(le/liǎo).PriorityQueue

// creates a priority queue of strings with integer priorities
var pq = new PriorityQueue();

// enqueue elements with associated priorities
pq.Enqueue("A", 3);
pq.Enqueue("B", 1);
pq.Enqueue("C", 2);
pq.Enqueue("D", 3);

pq.Dequeue(); // returns "B"
pq.Dequeue(); // returns "C"
pq.Dequeue(); // either "A" or "D", stability is not guaranteed.

感謝Patryk Golebiowski

更快地(dì / de)将結構體作爲(wéi / wèi)字典值處理

CollectionsMarshal.GetValueRef是(shì)一(yī / yì /yí)個(gè)新的(de)不(bù)安全API,它可以(yǐ)更快地(dì / de)更新字典中的(de)結構值。新 API 旨在(zài)用于(yú)高性能場景,而(ér)不(bù)是(shì)用于(yú)一(yī / yì /yí)般用途。它返回refstruct 值,然後可以(yǐ)使用典型技術就(jiù)地(dì / de)更新。

以(yǐ)下示例演示了(le/liǎo)如何使用新 API:

ref MyStruct value = CollectionsMarshal.GetValueRef(dictionary, key);
// Returns Unsafe.NullRef() if it doesn't exist; check using Unsafe.IsNullRef(ref value)
if (!Unsafe.IsNullRef(ref value))
{
    // Mutate in-place
    value.MyInt++;
}

在(zài)此更改之(zhī)前,更新struct字典值對于(yú)高性能場景可能會很昂貴,需要(yào / yāo)字典查找和(hé / huò)struct. 然後在(zài)更改 之(zhī)後struct,它将再次分配給字典鍵,從而(ér)導緻另一(yī / yì /yí)次查找和(hé / huò)複制操作。此改進将密鑰散列減少到(dào) 1(從 2)并删除所有結構複制操作。

歸功于(yú)本·亞當斯

新的(de)DateOnly和(hé / huò)TimeOnly結構

添加了(le/liǎo)僅限日期和(hé / huò)時(shí)間的(de)結構,具有以(yǐ)下特征:

  • 每個(gè)代表 a 的(de)一(yī / yì /yí)半DateTime,或者隻是(shì)日期部分,或者隻是(shì)時(shí)間部分。

  • DateOnly是(shì)生日、周年紀念日和(hé / huò)工作日的(de)理想選擇。它符合 SQL Server 的(de)date類型。

  • TimeOnly是(shì)定期會議、鬧鍾和(hé / huò)每周工作時(shí)間的(de)理想選擇。它符合 SQL Server 的(de)time類型。

  • 補充現有的(de)日期/時(shí)間類型 ( DateTime, DateTimeOffset, TimeSpan, TimeZoneInfo)。

  • 在(zài)System命名空間中,在(zài) CoreLib 中提供,就(jiù)像現有的(de)相關類型一(yī / yì /yí)樣。

性能改進 DateTime.UtcNow

這(zhè)種改進有以(yǐ)下好處:

  • 修複了(le/liǎo)在(zài) Windows 上(shàng)獲取系統時(shí)間的(de)2.5 倍性能回歸

  • 利用 5 分鍾的(de) Windows 閏秒數據滑動緩存,而(ér)不(bù)是(shì)在(zài)每次調用時(shí)獲取。

支持所有平台上(shàng)的(de) Windows 和(hé / huò) IANA 時(shí)區

這(zhè)種改進有以(yǐ)下好處:

  • 使用時(shí)的(de)隐式轉換(https://github.com/dotnet/runtime/pull/49412)TimeZoneInfo.FindSystemTimeZoneById

  • 通過新的(de)API顯式轉換上(shàng)TimeZoneInfo:TryConvertIanaIdToWindowsId,TryConvertWindowsIdToIanaId,和(hé / huò)HasIanaId(https://github.com/dotnet/runtime/issues/49407

  • 改進了(le/liǎo)使用不(bù)同時(shí)區類型的(de)系統之(zhī)間的(de)跨平台支持和(hé / huò)互操作。

  • 删除需要(yào / yāo)使用 TimeZoneConverter OSS 庫。該功能現已内置。

改進的(de)時(shí)區顯示名稱

Unix 上(shàng)的(de)時(shí)區顯示名稱已得到(dào)改進

  • 從 返回的(de)列表中的(de)顯示名稱中消除歧義。TimeZoneInfo.GetSystemTimeZones

  • 利用 ICU / CLDR 全球化數據。

  • 僅适用于(yú) Unix。Windows 仍然使用注冊表數據。這(zhè)可能會在(zài)以(yǐ)後更改。

還進行了(le/liǎo)以(yǐ)下額外改進:

  • UTC 時(shí)區的(de)顯示名稱和(hé / huò)标準名稱被硬編碼爲(wéi / wèi)英語,現在(zài)使用與其餘時(shí)區數據相同的(de)語言(CurrentUICulture在(zài) Unix 上(shàng),Windows 上(shàng)的(de)操作系統默認語言)。

  • 由于(yú)大(dà)小限制,Wasm 中的(de)時(shí)區顯示名稱改爲(wéi / wèi)使用非本地(dì / de)化的(de) IANA ID。

  • TimeZoneInfo.AdjustmentRule嵌套類将其BaseUtcOffsetDelta内部屬性公開并獲得一(yī / yì /yí)個(gè)新的(de)構造函數baseUtcOffsetDelta作爲(wéi / wèi)參數。(https://github.com/dotnet/runtime/issues/50256

  • TimeZoneInfo.AdjustmentRule 還獲得了(le/liǎo)在(zài) Unix 上(shàng)加載時(shí)區的(de)其他(tā)修複(https://github.com/dotnet/runtime/pull/49733),(https://github.com/dotnet/runtime/pull/50131

改進了(le/liǎo)對 Windows ACL 的(de)支持

System.Threading.AccessControl現在(zài)包括對與 Windows 訪問控制列表 (ACL) 交互的(de)改進支持。爲(wéi / wèi)、和(hé / huò)的(de)OpenExisting和(hé / huò)TryOpenExisting方法添加了(le/liǎo)新的(de)重載。這(zhè)些具有“安全權限”實例的(de)重載允許打開使用特殊 Windows 安全屬性創建的(de)線程同步對象的(de)現有實例。EventWaitHandleMutexSemaphore

此更新與 .NET Framework 中可用的(de) API 相匹配,并且具有相同的(de)行爲(wéi / wèi)。

以(yǐ)下示例演示如何使用這(zhè)些新 API。

對于(yú)Mutex:

var rights = MutexRights.FullControl;
string mutexName = "MyMutexName";

var security = new MutexSecurity();
SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
MutexAccessRule accessRule = new MutexAccessRule(identity, rights, AccessControlType.Allow);
security.AddAccessRule(accessRule);

// createdMutex, openedMutex1 and openedMutex2 point to the same mutex
Mutex createdMutex = MutexAcl.Create(initiallyOwned: true, mutexName, out bool createdNew, security);
Mutex openedMutex1 = MutexAcl.OpenExisting(mutexName, rights);
MutexAcl.TryOpenExisting(mutexName, rights, out Mutex openedMutex2);

爲(wéi / wèi)了(le/liǎo) Semaphore

var rights = SemaphoreRights.FullControl;
string semaphoreName = "MySemaphoreName";

var security = new SemaphoreSecurity();
SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
SemaphoreAccessRule accessRule = new SemaphoreAccessRule(identity, rights, AccessControlType.Allow);
security.AddAccessRule(accessRule);

// createdSemaphore, openedSemaphore1 and openedSemaphore2 point to the same semaphore
Semaphore createdSemaphore = SemaphoreAcl.Create(initialCount: 1,  maximumCount: 3, semaphoreName, out bool createdNew, security);
Semaphore openedSemaphore1 = SemaphoreAcl.OpenExisting(semaphoreName, rights);
SemaphoreAcl.TryOpenExisting(semaphoreName, rights, out Semaphore openedSemaphore2);

爲(wéi / wèi)了(le/liǎo) EventWaitHandle

var rights = EventWaitHandleRights.FullControl;
string eventWaitHandleName = "MyEventWaitHandleName";

var security = new EventWaitHandleSecurity();
SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
EventWaitHandleAccessRule accessRule = new EventWaitHandleAccessRule(identity, rights, AccessControlType.Allow);
security.AddAccessRule(accessRule);

// createdHandle, openedHandle1 and openedHandle2 point to the same event wait handle
EventWaitHandle createdHandle = EventWaitHandleAcl.Create(initialState: true, EventResetMode.AutoReset, eventWaitHandleName, out bool createdNew, security);
EventWaitHandle openedHandle1 = EventWaitHandleAcl.OpenExisting(eventWaitHandleName, rights);
EventWaitHandleAcl.TryOpenExisting(eventWaitHandleName, rights, out EventWaitHandle openedHandle2);

HMAC 一(yī / yì /yí)次性方法

HMAC類現在(zài)有靜态方法,允許HMACs的(de)一(yī / yì /yí)次性計算不(bù)分配。這(zhè)些添加類似于(yú)先前版本中添加的(de)用于(yú)哈希生成的(de)一(yī / yì /yí)次性方法。System.Security.Cryptography

DependentHandle 現在(zài)是(shì)公開的(de)

該DependentHandle類型現在(zài)是(shì)公開的(de),具有以(yǐ)下 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();
    }
}

它可用于(yú)創建高級系統,例如複雜的(de)緩存系統或該類型的(de)自定義版本。例如,MVVM 工具包中的(de)類型将使用它來(lái)避免廣播消息時(shí)的(de)内存分配。ConditionalWeakTableWeakReferenceMessenger

可移植線程池

在(zài).NET線程池已重新實現作爲(wéi / wèi)一(yī / yì /yí)個(gè)托管實現,現在(zài)作爲(wéi / wèi)默認的(de)線程池.NET 6.我們做出(chū)這(zhè)一(yī / yì /yí)改變,使所有的(de).NET應用程序能夠訪問線程池獨立的(de)是(shì)否相同正在(zài)使用 CoreCLR、Mono 或任何其他(tā)運行時(shí)。作爲(wéi / wèi)此更改的(de)一(yī / yì /yí)部分,我們沒有觀察到(dào)或預期任何功能或性能影響。

RyuJIT

該團隊對該版本的(de) .NET JIT 編譯器進行了(le/liǎo)許多改進,在(zài)每個(gè)預覽帖子(zǐ)中都有記錄。大(dà)多數更改都可以(yǐ)提高性能。這(zhè)裏涵蓋了(le/liǎo)一(yī / yì /yí)些 RyuJIT 的(de)亮點。

動态 PGO

在(zài) .NET 6 中,我們啓用了(le/liǎo)兩種形式的(de) PGO(配置文件引導優化):

  • 動态 PGO使用從當前運行收集的(de)數據來(lái)優化當前運行。

  • 靜态 PGO依靠從過去運行中收集的(de)數據來(lái)優化未來(lái)運行。

動态 PGO 已經在(zài)文章前面的(de)性能部分中介紹過。我會提供一(yī / yì /yí)個(gè)重新上(shàng)限。

動态 PGO 使 JIT 能夠在(zài)運行時(shí)收集有關實際用于(yú)該特定應用程序運行的(de)代碼路徑和(hé / huò)類型的(de)信息。然後 JIT 可以(yǐ)根據這(zhè)些代碼路徑優化代碼,有時(shí)會顯着提高性能。我們在(zài)測試和(hé / huò)生産方面都看到(dào)了(le/liǎo)健康的(de)兩位數改進。有一(yī / yì /yí)組經典的(de)編譯器技術,在(zài)沒有 PGO 的(de)情況下使用 JIT 或提前編譯是(shì)不(bù)可能的(de)。我們現在(zài)能夠應用這(zhè)些技術。熱/冷分裂是(shì)一(yī / yì /yí)種這(zhè)樣的(de)技術,去虛拟化是(shì)另一(yī / yì /yí)種技術。

要(yào / yāo)啓用動态 PGO,請在(zài)您的(de)應用程序将運行的(de)環境中進行設置。DOTNET_TieredPGO=1

如性能部分所述,動态 PGO 爲(wéi / wèi) TechEmpower JSON“MVC”套件的(de)每秒請求數提供了(le/liǎo) 26% 的(de)改進(510K -> 640K)。這(zhè)是(shì)一(yī / yì /yí)個(gè)驚人(rén)的(de)改進,無需更改代碼。

我們的(de)目标是(shì)在(zài) .NET 的(de)未來(lái)版本中默認啓用 Dynamic PGO,希望與 .NET 7 一(yī / yì /yí)起使用。我們強烈建議您在(zài)您的(de)應用程序中嘗試使用 Dynamic PGO 并向我們提供反饋。

完整的(de) PGO

要(yào / yāo)獲得動态 PGO 的(de)全部優勢,您可以(yǐ)設置兩個(gè)額外的(de)環境變量:和(hé / huò). 這(zhè)确保了(le/liǎo)盡可能多的(de)方法參與分層編譯。我們稱這(zhè)種變體爲(wéi / wèi)Full PGO。與動态 PGO 相比,完整 PGO 可以(yǐ)提供更大(dà)的(de)穩态性能優勢,但啓動時(shí)間會更慢(因爲(wéi / wèi)必須在(zài)第 0 層執行更多方法)。DOTNET_TC_QuickJitForLoops=1DOTNET_ReadyToRun=0

您不(bù)希望将此選項用于(yú)短期運行的(de)無服務器應用程序,但對于(yú)長期運行的(de)應用程序可能有意義。

在(zài)未來(lái)的(de)版本中,我們計劃精簡和(hé / huò)簡化這(zhè)些選項,以(yǐ)便您可以(yǐ)更簡單地(dì / de)獲得完整 PGO 的(de)好處,并适用于(yú)更廣泛的(de)應用程序。

靜态 PGO

我們目前使用靜态 PGO來(lái)優化 .NET 庫程序集,如随 R2R(準備運行)提供的(de)程序集。System.Private.CoreLib

靜态 PGO 的(de)好處在(zài)于(yú),當程序集使用 crossgen 編譯爲(wéi / wèi) R2R 格式時(shí),會進行優化。這(zhè)意味着在(zài)沒有運行時(shí)成本的(de)情況下有運行時(shí)優勢。例如,這(zhè)非常重要(yào / yāo),這(zhè)也(yě)是(shì) PGO 對 C++ 很重要(yào / yāo)的(de)原因。

循環對齊

内存對齊是(shì)現代計算中各種操作的(de)常見要(yào / yāo)求。在(zài) .NET 5 中,我們開始在(zài) 32 字節邊界對齊方法。在(zài) .NET 6 中,我們添加了(le/liǎo)一(yī / yì /yí)個(gè)功能來(lái)執行自适應循環對齊,該功能NOP在(zài)具有循環的(de)方法中添加填充指令,以(yǐ)便循環代碼從 mod(16) 或 mod(32) 内存地(dì / de)址開始。這(zhè)些更改提高并穩定了(le/liǎo) .NET 代碼的(de)性能。

在(zài)下面的(de)冒泡排序圖中,數據點 1 表示我們開始在(zài) 32 字節邊界對齊方法的(de)點。數據點 2 表示我們也(yě)開始對齊内部循環的(de)點。如您所見,基準測試的(de)性能和(hé / huò)穩定性都有顯着提高。





硬件加速結構

結構是(shì) CLR 類型系統的(de)重要(yào / yāo)組成部分。近年來(lái),它們經常被用作整個(gè) .NET 庫中的(de)性能原語。最近的(de)例子(zǐ)是(shì)ValueTask,ValueTuple和(hé / huò)。記錄結構是(shì)一(yī / yì /yí)個(gè)新的(de)例子(zǐ)。在(zài) .NET 5 和(hé / huò) .NET 6 中,我們一(yī / yì /yí)直在(zài)提高結構的(de)性能,部分是(shì)通過确保結構可以(yǐ)保存在(zài)超快的(de) CPU 寄存器中,當它們是(shì)局部變量、參數或方法的(de)返回值時(shí))。這(zhè)對于(yú)使用向量計算的(de) API 尤其有益。Span

穩定性能測量

團隊中有大(dà)量的(de)工程系統工作從未出(chū)現在(zài)博客上(shàng)。這(zhè)适用于(yú)您使用的(de)任何硬件或軟件産品。JIT 團隊開展了(le/liǎo)一(yī / yì /yí)個(gè)項目來(lái)穩定性能測量,目标是(shì)增加我們内部性能實驗室自動化自動報告的(de)回歸值。這(zhè)個(gè)項目很有趣,因爲(wéi / wèi)進行了(le/liǎo)深入的(de)調查以(yǐ)及實現穩定性所需的(de)産品更改。它還展示了(le/liǎo)我們衡量保持和(hé / huò)提高績效的(de)規模。





此圖像展示了(le/liǎo)不(bù)穩定的(de)性能測量,其中性能在(zài)連續運行中在(zài)慢速和(hé / huò)快速之(zhī)間波動。x 軸是(shì)測試日期,y 軸是(shì)測試時(shí)間(以(yǐ)納秒爲(wéi / wèi)單位)。在(zài)圖表的(de)末尾(在(zài)提交這(zhè)些更改之(zhī)後),您可以(yǐ)看到(dào)測量值穩定下來(lái),結果最好。此圖像演示了(le/liǎo)單個(gè)測試。還有更多測試在(zài)dotnet/runtime #43227中被證明具有類似的(de)行爲(wéi / wèi)。

準備運行的(de)代碼 / Crossgen 2

Crossgen2 是(shì)crossgen 工具的(de)替代品。它旨在(zài)滿足兩個(gè)結果:

  • 使跨代開發更高效。

  • 啓用一(yī / yì /yí)組當前無法通過 crossgen 實現的(de)功能。

這(zhè)種轉換有點類似于(yú)本機代碼 csc.exe 到(dào)托管代碼Roslyn 編譯器。Crossgen2 是(shì)用 C# 編寫的(de),但是(shì),它沒有像 Roslyn 那樣公開花哨的(de) API。

我們可能已經/已經爲(wéi / wèi) .NET 6 和(hé / huò) 7 計劃了(le/liǎo)六個(gè)依賴于(yú) crossgen2 的(de)項目。矢量指令默認值的(de)提議是(shì)我們想要(yào / yāo)爲(wéi / wèi) .NET 6 進行的(de) crossgen2 功能和(hé / huò)産品更改的(de)一(yī / yì /yí)個(gè)很好的(de)例子(zǐ),但更有可能是(shì) .NET 7。版本氣泡是(shì)另一(yī / yì /yí)個(gè)很好的(de)例子(zǐ)。

Crossgen2 支持跨操作系統和(hé / huò)架構維度的(de)交叉編譯(因此得名“crossgen”)。這(zhè)意味着您将能夠使用單個(gè)構建機器爲(wéi / wèi)所有目标生成本機代碼,至少與準備運行的(de)代碼相關。然而(ér),運行和(hé / huò)測試該代碼是(shì)另一(yī / yì /yí)回事,爲(wéi / wèi)此您需要(yào / yāo)适當的(de)硬件和(hé / huò)操作系統。

第一(yī / yì /yí)步是(shì)用crossgen2編譯平台本身。我們使用 .NET 6 完成了(le/liǎo)所有架構。因此,我們能夠在(zài)此版本中淘汰舊的(de) crossgen。請注意,crossgen2 僅适用于(yú) CoreCLR,不(bù)适用于(yú)基于(yú) Mono 的(de)應用程序(它們具有一(yī / yì /yí)組單獨的(de)代碼生成工具)。

這(zhè)個(gè)項目——至少在(zài)開始時(shí)——并不(bù)以(yǐ)性能爲(wéi / wèi)導向。目标是(shì)爲(wéi / wèi)托管 RyuJIT(或任何其他(tā))編譯器提供更好的(de)架構,以(yǐ)離線方式(不(bù)需要(yào / yāo)或啓動運行時(shí))生成代碼。

您可能會說(shuō)“嘿……如果 crossgen2 是(shì)用 C# 編寫的(de),那麽您不(bù)必啓動運行時(shí)來(lái)運行它嗎?” 是(shì)的(de),但這(zhè)不(bù)是(shì)本文中“離線”的(de)意思。當 crossgen2 運行時(shí),我們沒有使用 crossgen2 運行時(shí)附帶的(de) JIT 來(lái)生成準備運行 (R2R) 代碼. 那是(shì)行不(bù)通的(de),至少對于(yú)我們的(de)目标是(shì)行不(bù)通的(de)。想象 crossgen2 在(zài) x64 機器上(shàng)運行,我們需要(yào / yāo)爲(wéi / wèi) Arm64 生成代碼。Crossgen2 加載 Arm64 RyuJIT(爲(wéi / wèi) x64 編譯)作爲(wéi / wèi)本機插件,然後使用它生成 Arm64 R2R 代碼。機器指令隻是(shì)保存到(dào)文件中的(de)字節流。它也(yě)可以(yǐ)反方向工作。在(zài) Arm64 上(shàng),crossgen2 可以(yǐ)使用編譯爲(wéi / wèi) Arm64 的(de) x64 RyuJIT 生成 x64 代碼。我們使用相同的(de)方法在(zài) x64 機器上(shàng)定位 x64 代碼。Crossgen2 加載爲(wéi / wèi)任何需要(yào / yāo)的(de)配置構建的(de) RyuJIT。這(zhè)可能看起來(lái)很複雜,但如果您想啓用無縫的(de)交叉目标模型,它就(jiù)是(shì)您需要(yào / yāo)的(de)那種系統,而(ér)這(zhè)正是(shì)我們想要(yào / yāo)的(de)。

我們希望隻在(zài)一(yī / yì /yí)個(gè)版本中使用“crossgen2”這(zhè)個(gè)術語,之(zhī)後它将取代現有的(de) crossgen,然後我們将回到(dào)對“crossgen2”使用“crossgen”這(zhè)個(gè)術語。

.NET 診斷:EventPipe

EventPipe 是(shì)我們的(de)跨平台機制,用于(yú)在(zài)進程内或進程外輸出(chū)事件、性能數據和(hé / huò)計數器。從 .NET 6 開始,我們已将實現從 C++ 移至 C。通過此更改,Mono 也(yě)使用 EventPipe。這(zhè)意味着 CoreCLR 和(hé / huò) Mono 使用相同的(de)事件基礎結構,包括 .NET 診斷 CLI 工具。

這(zhè)一(yī / yì /yí)變化還伴随着 CoreCLR 的(de)小幅縮小:


我們還進行了(le/liǎo)一(yī / yì /yí)些更改,以(yǐ)在(zài)負載下提高 EventPipe 吞吐量。在(zài)最初的(de)幾個(gè)預覽版中,我們進行了(le/liǎo)一(yī / yì /yí)系列更改,使吞吐量提高了(le/liǎo) .NET 5 所能達到(dào)的(de) 2.06 倍:





對于(yú)此基準測試,越高越好。.NET 6 是(shì)橙色線,.NET 5 是(shì)藍色線。

開發工具包

對 .NET SDK 進行了(le/liǎo)以(yǐ)下改進。

.NET 6 SDK 可選工作負載的(de) CLI 安裝

.NET 6 引入了(le/liǎo)SDK 工作負載的(de)概念。工作負載是(shì)可選組件,可以(yǐ)安裝在(zài) .NET SDK 之(zhī)上(shàng)以(yǐ)啓用各種方案。.NET 6 中的(de)新工作負載是(shì):.NET MAUI 和(hé / huò) Blazor WebAssembly AOT 工作負載。我們可能會在(zài) .NET 7 中創建新的(de)工作負載(可能來(lái)自現有的(de) SDK)。工作負載的(de)最大(dà)好處是(shì)尺寸減小和(hé / huò)可選性。我們希望随着時(shí)間的(de)推移使 SDK 變得更小,并允許隻安裝您需要(yào / yāo)的(de)組件。這(zhè)個(gè)模型對開發者機器有好處,甚至對 CI 更好。

Visual Studio 用戶實際上(shàng)不(bù)需要(yào / yāo)擔心工作負載。工作負載功能的(de)設計目的(de)是(shì)讓安裝協調器(如 Visual Studio)可以(yǐ)爲(wéi / wèi)您安裝工作負載。可以(yǐ)通過 CLI 直接管理工作負載。

工作負載功能公開了(le/liǎo)多個(gè)用于(yú)管理工作負載的(de)動詞,包括以(yǐ)下内容:

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

  • dotnet workload install — 安裝命名的(de)工作負載。

  • dotnet workload list — 列出(chū)您已安裝的(de)工作負載。

  • dotnet workload update — 将所有已安裝的(de)工作負載更新到(dào)最新的(de)可用版本。

該update動詞查詢更新的(de)工作負載清單、更新本地(dì / de)清單、下載已安裝工作負載的(de)新版本,然後删除工作負載的(de)所有舊版本。這(zhè)類似于(yú)(在(zài)基于(yú) Debian 的(de) Linux 發行版上(shàng)使用)。将工作負載視爲(wéi / wèi) SDK 的(de)私有包管理器是(shì)合理的(de)。它是(shì)私有的(de),因爲(wéi / wèi)它僅可用于(yú) SDK 組件。我們将來(lái)可能會重新考慮這(zhè)一(yī / yì /yí)點。nuget.orgapt update && apt upgrade -y

這(zhè)些dotnet workload命令在(zài)給定 SDK 的(de)上(shàng)下文中運行。假設您同時(shí)安裝了(le/liǎo) .NET 6 和(hé / huò) .NET 7。工作負載命令将爲(wéi / wèi)每個(gè) SDK 提供不(bù)同的(de)結果,因爲(wéi / wèi)工作負載會有所不(bù)同(至少相同工作負載的(de)不(bù)同版本)。

請注意,dotnet workload install将工作負載從 NuGet.org 複制到(dào)您的(de) SDK 安裝中,因此sudo如果 SDK 安裝位置受到(dào)保護(意味着在(zài)管理員/根位置),則需要(yào / yāo)提升運行或使用。

内置SDK版本檢查

爲(wéi / wèi)了(le/liǎo)更輕松地(dì / de)跟蹤新版本的(de) SDK 和(hé / huò)運行時(shí)何時(shí)可用,我們向 .NET 6 SDK 添加了(le/liǎo)一(yī / yì /yí)個(gè)新命令。

dotnet sdk check

它會告訴您是(shì)否有更新版本可用于(yú)您已安裝的(de)任何 .NET SDK、運行時(shí)或工作負載。您可以(yǐ)在(zài)下圖中看到(dào)新體驗。





dotnet new

您現在(zài)可以(yǐ)在(zài)http://NuGet.org中搜索帶有.dotnet new --search

模闆安裝的(de)其他(tā)改進包括支持開關以(yǐ)支持私有 NuGet 源的(de)授權憑據。--interactive

安裝 CLI 模闆後,您可以(yǐ)通過和(hé / huò)檢查更新是(shì)否可用。--update-check--update-apply

NuGet 包驗證

包驗證工具使 NuGet 庫開發人(rén)員能夠驗證他(tā)們的(de)包是(shì)否一(yī / yì /yí)緻且格式良好。

這(zhè)包括:

  • 驗證跨版本沒有重大(dà)更改。

  • 驗證包對于(yú)所有特定于(yú)運行時(shí)的(de)實現是(shì)否具有相同的(de)公共 API 集。

  • 确定任何目标框架或運行時(shí)适用性差距。

此工具是(shì) SDK 的(de)一(yī / yì /yí)部分。使用它的(de)最簡單方法是(shì)在(zài)項目文件中設置一(yī / yì /yí)個(gè)新屬性。

 true

更多 Roslyn 分析儀

在(zài) .NET 5 中,我們随 .NET SDK 提供了(le/liǎo)大(dà)約 250 個(gè)分析器。其中許多已經存在(zài),但作爲(wéi / wèi) NuGet 包在(zài)帶外發布。我們爲(wéi / wèi) .NET 6 添加了(le/liǎo)更多分析器

默認情況下,大(dà)多數新分析器在(zài)信息級别啓用。您可以(yǐ)通過啓用這(zhè)些分析儀在(zài)警告級别配置的(de)分析模式是(shì)這(zhè)樣的(de):。All

我們發布了(le/liǎo)我們想要(yào / yāo)的(de) .NET 6 分析器集(加上(shàng)一(yī / yì /yí)些額外的(de)東西),然後将其中的(de)大(dà)部分都準備好了(le/liǎo)。社區添加了(le/liǎo)幾個(gè)實現,包括這(zhè)些。

感謝Meik Tranel和(hé / huò)Newell Clark

爲(wéi / wèi)平台兼容性分析器啓用自定義防護

該CA1416平台兼容性分析儀已經可以(yǐ)識别使用的(de)方法平台警衛OperatingSystem和(hé / huò)RuntimeInformation,如和(hé / huò)。但是(shì),分析器不(bù)識别任何其他(tā)保護可能性,例如緩存在(zài)字段或屬性中的(de)平台檢查結果,或者在(zài)輔助方法中定義了(le/liǎo)複雜的(de)平台檢查邏輯。OperatingSystem.IsWindowsOperatingSystem.IsWindowsVersionAtLeast

爲(wéi / wèi)了(le/liǎo)允許自定義保護的(de)可能性,我們添加了(le/liǎo)新屬性SupportedOSPlatformGuard并UnsupportedOSPlatformGuard使用相應的(de)平台名稱和(hé / huò)/或版本注釋自定義保護成員。該注釋被平台兼容性分析器的(de)流分析邏輯識别和(hé / huò)尊重。

用法

    [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。它是(shì)另一(yī / yì /yí)個(gè)巨大(dà)的(de) .NET 版本,在(zài)性能、功能、可用性和(hé / huò)安全性方面的(de)改進幾乎相同。我們希望您找到(dào)許多改進,最終使您在(zài)日常開發中更有效率和(hé / huò)能力,并提高性能或降低生産應用程序的(de)成本。我們已經開始聽到(dào)你們中那些已經開始使用 .NET 6 的(de)人(rén)的(de)好消息。

在(zài) Microsoft,我們也(yě)處于(yú) .NET 6 部署的(de)早期階段,一(yī / yì /yí)些關鍵應用程序已經投入生産,未來(lái)幾周和(hé / huò)幾個(gè)月内還有更多應用程序即将推出(chū)。

.NET 6 是(shì)我們最新的(de) LTS 版本。我們鼓勵所有人(rén)轉向它,特别是(shì)如果您使用 .NET 5。我們期待它成爲(wéi / wèi)有史以(yǐ)來(lái)采用速度最快的(de) .NET 版本。

此版本是(shì)至少 1000 人(rén)(但可能更多)的(de)結果。這(zhè)包括來(lái)自 Microsoft 的(de) .NET 團隊以(yǐ)及社區中的(de)更多人(rén)。我試圖在(zài)這(zhè)篇文章中包含許多社區貢獻的(de)功能。感謝您花時(shí)間創建這(zhè)些并完成我們的(de)流程。我希望這(zhè)次經曆是(shì)好的(de),更多的(de)人(rén)會做出(chū)貢獻。

這(zhè)篇文章是(shì)許多有才華的(de)人(rén)合作的(de)結果。這(zhè)些貢獻包括團隊在(zài)整個(gè)發布過程中提供的(de)功能内容、爲(wéi / wèi)此最終帖子(zǐ)創建的(de)重要(yào / yāo)新内容,以(yǐ)及使最終内容達到(dào)您應得的(de)質量所需的(de)大(dà)量技術和(hé / huò)散文更正。很高興爲(wéi / wèi)您制作它和(hé / huò)所有其他(tā)帖子(zǐ)。

感謝您成爲(wéi / wèi) .NET 開發人(rén)員。


來(lái)源:知乎