Liam W
封面

如约而至,.NET 5.0 今天正式发布

作者
王亮·发表于 3 年前

作者:Richard
翻译:精致码农-王亮
原文:http://dwz.win/WFz
说明:文章太长,后面关于 C# 9 语言新特性及平台新特性的部分没有翻译,这部分基本都是代码示例,请直接阅读原文。关于 C# 9 新特性的介绍也可以阅读我的系列文章[C# 9.0 新特性]

我们很高兴今天(美:11 月 10 日,中:11 月 11 日)能发布 .NET 5.0。这是一个重要的版本–包括 C# 9 和 F# 5–具有一系列广泛的新功能和引人注目的改进。微软和其他公司的团队已经在生产和性能测试中积极使用它。这些团队向我们展示了巨大的成果,展示了性能的提升、降低了的 Web 应用程序托管成本。从 Preview 1 开始,我们已经在 5.0 上运行了自己的网站。从我们目前的所见所闻来看,.NET 5.0 带来了巨大的价值,而且升级无需进行较大的改动。对于你的下一个应用程序来说,它是一个很好的选择,并且可以从早期的 .NET Core 版本直接升级。我们希望能让你喜欢在台式机、笔记本电脑和云计算实例中使用它。

ASP.NET Core 和 EF Core 也在今天发布。

你可以下载适用于 Windows,macOS 和 Linux 的 .NET 5.0(适用于 x86、x64、Arm32 和 Arm64 架构的 CPU ):

对于 Visual Studio 用户,你需要Visual Studio 16.8或更高版本才能在 Windows 上使用.NET 5.0,并在 macOS 上使用 Visual Studio for Mac的最新版本。在 C#扩展Visual Studio 代码已经支持.NET 5.0 和 C#9。

.NET 5.0 是我们.NET 统一历程中的第一个版本。我们构建.NET 5.0 是为了让更多的开发者能够将他们的.NET Framework 代码和应用程序迁移到.NET 5.0 中。我们还在 5.0 中完成了许多早期工作,以便 Xamarin 开发人员能够在我们发布.NET 6.0 时使用统一的.NET 平台。关于.NET 统一的问题,后面还有更多内容。

现在是一个很好的时机来呼吁与每个为.NET 项目做出贡献的人进行美好的合作。现在官方的 GitHub中有大量的个人和大大小小的公司(包括.NET 基金会公司赞助商)组成的大型社区,就.NET 的各个方面共同协作。.NET 5.0 的改进是许多人的努力、聪明的想法以及他们对平台的关心和热爱的结果,所有这些都超过了微软对项目的管理。我们向每一位为.NET 5.0(以及之前的版本)做出贡献的人致以深深的谢意。

我们早在 2019 年 5 月就引入了.NET 5.0,甚至还设定了 2020 年 11 月的发布日期。在那篇文章中可以看到:“我们将在今年 9 月发布.NET Core 3.0,在 2020 年 11 月发布.NET 5,然后我们打算每年 11 月发布一次主要版本的.NET。” 你会认为 "2020 年 11 月 "是一张无法兑现的支票,因为今年面临着种种挑战。然而,.NET 5.0 已经按时发布。感谢团队中的每一个人,让这一切得以实现!我知道这并不容易。展望未来,你应该期待 2021 年 11 月的.NET 6.0。我们打算每年 11 月发布新的.NET 版本。

本篇博客的其余部分专门强调并详细介绍了.NET 5.0 中的大部分改进。此外,还更新了我们的.NET 统一愿景。

.NET 5.0 亮点

.NET 5.0 有许多重要的改进

我为.NET 5.0 预览文章编写了许多示例。你可能想看看 .NET 5.0 示例以了解有关 C#9 和库的新功能的更多信息。

平台和微软支持

.NET 5.0 具有与 .NET Core 3.1 几乎相同的平台支持矩阵,适用于 Windows、macOS 和 Linux。如果你在受支持的操作系统上使用 .NET Core 3.1,你应该能够在同一操作系统版本上采用 .NET 5.0 的大部分内容。.NET 5.0 最重要的新增功能是 Windows Arm64。

.NET 5.0 是一个当前版本。这意味着它将在.NET 6.0 发布后的三个月内得到支持。因此,我们预计将支持 .NET 5.0 到 2022 年 2 月中旬。.NET 6.0 将是一个 LTS 版本,并将像 .NET Core 3.1 一样支持三年。

统一平台愿景

去年,我们分享了一个统一的 .NET 协议栈和生态系统的愿景。对你的价值在于,你将能够使用一套单一的 API、语言和工具来针对广泛的应用类型,包括移动、云、桌面和物联网。你可能会意识到,今天你已经可以使用 .NET 瞄准一组广泛的平台,然而,工具和 API 在 Web 和 Mobile 之间并不总是相同的,也不总是同时发布的。

作为 .NET 5.0 和 6.0 的一部分,我们正在将 .NET 统一为一个单一的产品体验,同时使你能够只选择你想要使用的 .NET 平台的一部分。如果你想针对 Mobile 而不是 WebAssembly,你不需要下载 WebAssembly 工具,反之亦然,ASP.NET Core 和 WPF 也一样。你也将有一个更简单的方法从命令行获取所有你需要的 .NET 工具和运行时包。我们正在为 .NET 平台组件启用包管理器体验(包括使用现有的包管理器)。这对于很多场景来说都会有很大的帮助。开发环境的快速构建和 CI/CD 可能会是最大的受益者。

我们原本打算用 .NET 5.0 来提供全部的统一愿景,但在全球大流行之后,我们不得不适应客户不断变化的需求。我们一直在与来自世界各地的公司团队合作,这些公司需要帮助他们加快采用云技术。他们也必须适应客户不断变化的需求。因此,我们正在通过两个版本来实现这一愿景。

实现这一愿景的第一步是整合.NET 资源库,包括 Mono 的大部分子集。为 .NET 的运行时和库提供一个资源库是在所有地方提供相同产品的前提条件。它还有助于进行影响运行时和库的广泛更改,而以前的版本库是有边界的。有些人担心一个大的 repo 会更难管理,事实证明并非如此。

在 .NET 5.0 版本中,Blazor 是利用 repo 整合和.NET 统一的最好例子。现在,Blazor WebAssembly的运行时和库都是由合并后的dotnet/runtime repo 构建的。这意味着 Blazor WebAssembly 和服务器上的 Blazor 使用完全相同的代码,例如List<T>。在.NET 5.0 之前,Blazor 的情况并非如此。我们对 Blazor WebAssembly 采用的方法与我们在 .NET 6.0 中对 Xamarin 采用的方法非常相似。

.NET 框架仍然是微软支持的产品,并将继续支持每个新版本的 Windows。我们去年宣布,我们已经停止向 .NET Framework 添加新功能,并完成了向 .NET Core 添加 .NET Framework API 的工作。这意味着,现在是考虑将你的 .NET Framework 应用程序转移到 .NET Core 的好时机。对于 .NET Framework 客户端开发者来说,.NET 5.0 支持 Windows Forms 和 WPF。我们从许多开发人员那里听说,可以直接从 .NET Framework 移植。对于.NET Framework 服务器开发者来说,你需要采用 ASP.NET Core 来使用 .NET 5.0。对于 Web Forms 开发人员来说,我们认为 Blazor 提供了类似的开发体验,其实现效率更高、更现代化。WCF 服务器和工作流用户可以寻找支持这些框架的社区项目。从 .NET Framework 到 .NET Core 的移植文档是一个很好的开始。说了这么多,如果你对自己的体验感到满意的话,将你的应用保持在 .NET Framework 上是一个不错的方法。

Windows 团队正在研究 Project Reunion,作为 UWP 和相关技术的下一步任务。我们一直在与 Reunion 团队合作,以确保 .NET 5.0 及以后的版本能够与 WinUI 和 WebView2 良好地配合。

让我们来看看 5.0 版本有哪些新内容。

语言特点

C# 9 和 F# 5 是 .NET 5.0 版本的一部分,包含在 .NET 5.0 SDK 中。Visual Basic 也包含在 5.0 SDK 中,它不包括语言变化,但有一些改进,以支持 .NET Core 上的 Visual Basic 应用框架。

C# Source Generators 是一个重要的新 C#编译器功能。它们在技术上不是 C# 9 的一部分,因为它没有任何语言语法。请参阅 New C# Source Generator Samples 来帮助你开始使用这个新功能。我们期望在 .NET 6.0 及以后的 .NET 产品中更多地使用源码生成器。

作为我们自己试用新版本的一种方式,我们中的一些人决定更新 dotnet/iot repo,以使用新的 C# 9 语法并以 .NET 5.0 为 target。它使用了顶层程序、记录、模式和 switch 表达式。它也已经更新,以利用 .NET 库中完整的可空注解特性。我们将从该 repo 中的几个例子来探索 C# 9。

顶级程序

led-blink 程序是一个不错的紧凑顶级程序示例。

using System;
using System.Device.Gpio;
using System.Threading;

var pin = 18;
var lightTime = 1000;
var dimTime = 200;

Console.WriteLine(
quot;Let's blink an LED!"
); using GpioController controller = new (); controller.OpenPin(pin, PinMode.Output); Console.WriteLine(
quot;GPIO pin enabled for use:
{pin}"
); // turn LED on and off while (true) { Console.WriteLine(
quot;Light for
{lightTime}ms"
); controller.Write(pin, PinValue.High); Thread.Sleep(lightTime); Console.WriteLine(
quot;Dim for
{dimTime}ms"
); controller.Write(pin, PinValue.Low); Thread.Sleep(dimTime); }

你还可以看到 target-typed 的使用以及newcontroller变量的赋值。的GpioController类型仅在分配的左手侧所定义。类型是在右侧推断的。此新语法是的替代方法var,后者的类型仅显示在分配的右侧,并通过var关键字在左侧进行推断。

通过定义方法并利用同一文件或其他文件中定义的类型,顶级程序的复杂性也可能增加。该CharacterLcd示例演示了其中的一些功能。

逻辑和属性模式

C#9 包括对新模式的支持。你可以在以下代码中CCS811 气体传感器中看到逻辑模式的示例。

var threshChoice = Console.ReadKey();
Console.WriteLine();
if (threshChoice.KeyChar is 'Y' or 'y')
{
   TestThresholdAndInterrupt(ccs811);
}

另一个新模式是属性模式。你可以在Mycroft Information Access 6.0示例中看到几个属性检查。在下面的代码是从所拍摄的PN532 RFID 和 NFC 读取器样品。

if (pollingType is not { Length: >15 })
{
      return null;
}

此代码测试pollingType(键入为)不为 null 且不包含> 15 个字节。byte[]

荣誉奖:

记录

C#9 包括一种称为记录的新型类。与常规类相比,它具有许多优点,其中一半与更简洁的语法有关。在下面的记录是取自Bh1745 RGB 传感器结合。

public record ChannelCompensationMultipliers(double Red, double Green, double Blue, double Clear);

然后稍后在同一文件中以熟悉的语法使用它:

ChannelCompensationMultipliers = new (2.2, 1.0, 1.8, 10.0);

可空性注释改进

.NET 库现在已完全注释为可空性。这意味着,如果启用 nullability,你将从平台上获取更多类型信息,以指导你对该功能的使用。目前,.NET 文档尚未完全注释。例如,应该注释为,而[String.Split(Char ])的注释为。我们希望这一问题能尽快解决。完整的信息可在source.dot.net 上以及通过 Visual Studio 中的 F12 元数据查找获得。String.IsNullOrEmpty(string)string?``char[]?

System.Device.GpioIot.Device.Bindings包(版本 1.1.0 两者)也被标注为这个版本的一部分,使用更新后的.NET 5.0 的注解。这些库都是多目标的,但是,我们使用 5.0 视图为所有目标生成注释。

注意:将现有的.NET Core 3.1 代码重新定位为.NET 5.0 时,可能会生成新的诊断(如果启用了可空性),因为其中包含新的注释。

我们还添加了新的注释类型。大类通常在从构造函数调用的帮助器方法中实例化对象成员。C#编译器无法遵循对对象分配的调用流程。退出构造函数时,它会认为该成员为 null,并使用发出警告CS8618。该MemberNotNull属性可解决此问题。你将该属性应用于助手方法。然后,编译器将看到你设置了该值,并意识到该方法是从构造函数中调用的。MemberNotNullWhen相似。

你可以使用以下代码MemberNotNullBMxx80 温度传感器中看到一个示例。

[MemberNotNull(nameof(_calibrationData))]
private void ReadCalibrationData()
{
   switch (this)
   {
         case Bme280 _:
            _calibrationData = new Bme280CalibrationData();
            _controlRegister = (byte)Bmx280Register.CTRL_MEAS;
            break;
         case Bmp280 _:
            _calibrationData = new Bmp280CalibrationData();
            _controlRegister = (byte)Bmx280Register.CTRL_MEAS;
            break;
         case Bme680 _:
            _calibrationData = new Bme680CalibrationData();
            _controlRegister = (byte)Bme680Register.CTRL_MEAS;
            break;
         default:
            throw new Exception("Bmxx80 device not correctly configured. Could not find calibraton data.");
   }

   _calibrationData.ReadFromDevice(this);
}

实际的代码使用条件编译。这是因为项目是多目标的,并且.NET 5.0+仅支持此属性。使用该属性可以跳过运行时检查(在构造函数中),否则将需要满足空值要求,如早期.NET 版本那样

工具类

我们改进了 Windows 窗体设计器,更改了目标框架适用于.NET 5.0 及更高版本的方式,更改了 WinRT 的支持方式,并进行了其他改进。

Windows 窗体设计器

Windows Forms 设计器(用于.NET Core 3.1 和.NET 5.0)已经在 Visual Studio 16.8 中进行了更新,现在支持所有 Windows Forms 控件。它还支持所有 Telerik 控件。设计器包括你期望的所有设计器功能,包括:拖放,选择,移动和调整大小,剪切/复制/粘贴/删除控件,与属性窗口集成,事件生成等。数据绑定和对更广泛的第三方控件的支持即将到来。

Windows窗体设计器

Windows Forms Designer for .NET Core 发布中了解更多信息。

.NET 5.0 目标框架

我们已经更改了.NET 5.0用于目标框架的方法。以下项目文件演示了新的.NET 5.0 目标框架。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

</Project>

到目前为止,新表单比我们使用的样式更紧凑,更直观。另外,我们正在扩展目标框架以描述操作系统依赖性。我们希望通过.NET 6.0 中的 Xamarin 定位 iOS 和 Android,从而推动了这一变化。net5.0``netcoreapp3.1

Windows 桌面 API(包括 Windows 窗体,WPF 和 WinRT)仅在定位时可用。你可以指定操作系统版本,例如或(对于Windows October 2018 Update)。如果要使用WinRT API,则需要定位 Windows 10 版本。net5.0-windows``net5.0-windows7``net5.0-windows10.0.17763.0

使用新的 TFM 时,跨平台方案可能会更具挑战性。例如,如果你要避免为 Windows 构建或避免在 Linux 上提取 Windows 运行时程序包,System.Device.Gpio演示了一种管理 Windows 目标框架的模式net5.0-windows

变更摘要:

  • net5.0 是.NET 5.0 的新目标框架 Moniker(TFM)。
  • net5.0结合并替换netcoreappnetstandardTFM。
  • net5.0支持.NET Framework 兼容模式
  • net5.0-windows 将用于公开 Windows 特定功能,包括 Windows 窗体,WPF 和 WinRT API。
  • .NET 6.0 将使用相同的方法,并带有和将添加和。net6.0``net6.0-ios``net6.0-android
  • 特定于操作系统的 TFM 可以包含操作系统版本号,例如。net6.0-ios14
  • 可移植的 API(如 ASP.NET Core)可与一起使用。带有的 Xamarin 形式也是如此。net5.0``net6.0

Visual Studio 16.8 中的模板仍针对控制台,WPF 和 Windows Forms 应用程序以.NET Core 3.1 为目标。ASP.NET 模板已更新为支持.NET 5.0。我们将在 Visual Studio 16.9 中为其余模板更新模板。

WinRT Interop(重大更改)

在以 Windows API 为目标这一主题上,我们已移至一个新模型,以作为.NET 5.0 的一部分来支持 WinRT API。这包括调用 API(在任一方向上; CLR <==> WinRT),两个类型系统之间的数据封送处理以及打算在类型系统或ABI边界上统一对待的类型的统一(即“投影类型” ”,并且是示例)。IEnumerableIIterable

现有的 WinRT 互操作体系已被除去从.NET 运行时作为.NET 5.0 的一部分。这是一个巨大的变化。这意味着使用 WinRT 和.NET Core 3.x 的应用程序和库将需要重建,并且不能按原样在.NET 5.0 上运行。使用 WinRT API 的库将需要多目标来管理.NET Core 3.1 和.NET 5.0 之间的这种差异。

展望未来,我们将依靠 Windows 中的 WinRT 团队提供的新CsWinRT 工具。它生成基于 C#的 WinRT 互操作程序集,可以通过 NuGet 交付该程序集。Windows 团队正是针对 Windows 中的 WinRT API 所做的。希望将 WinRT(在 Windows 上)用作互操作系统的任何人都可以使用该工具,以将本机 API 公开给.NET 或将.NET API 公开给本机代码。

CsWinRT 工具在逻辑上与 tlbimp 和 tlbexp 相似,但是要好得多。tlb 工具依赖于.NET 运行时中的许多 COM 互操作管道。CsWinRT 工具仅依赖于公共.NET API。就是说,C#9 中的函数指针功能(部分在.NET 5.0 运行时中实现)在某种程度上受到 CsWinRT 工具需求的启发。

这种新的 WinRT 互操作模型有几个好处:

  • 可以独立于.NET 运行时进行开发和改进。
  • 它与为其他操作系统(如 iOS 和 Android)提供的基于工具的互操作系统对称。
  • 该工具可以利用其他.NET 功能(AOT,C#功能,IL 链接),而以前的系统则不提供该功能。
  • 简化.NET 运行时代码库

你无需添加 NuGet 引用即可使用 WinRT API。瞄准 Windows 10 TFM(在前面的.NET 5.0 TFM 部分中已经讨论过)就足够了。如果目标是.NET Core 3.1 或更早版本,则需要引用 WinRT 软件包。你可以在System.Device.Gpio 项目中看到此模式。

本地出口

我们已经请求为导出本机二进制文件启用导出,而本机二进制文件在很长一段时间内都已调用.NET 代码。该方案的构建块是托管UnmanagedCallersOnlyAttribute 的API 支持

此功能是创建更高级别体验的基础。我们团队的Aaron Robinson一直在从事.NET Native Exports项目,该项目为将.NET 组件作为本机库发布提供了更完整的体验。我们正在寻找有关此功能的反馈,以帮助你确定该方法是否应包含在产品中。

.NET Native 导出项目使你能够:

  • 公开自定义的本地出口。
  • 不需要像 COM 这样的高级互操作技术。
  • 跨平台工作。

现有的项目可以实现类似的方案,例如:

多年来,我们已经在本机应用程序中看到了多种.NET 托管模型。@rseanhall 为此提出并实现了一个新颖的新模型,该模型利用了.NET 应用程序托管层提供的所有内置应用程序功能(特别是加载依赖项),同时允许从本机代码调用自定义入口点。对于许多情况而言,这是完美的选择,并且可以想象它在托管来自本机应用程序的.NET 组件的开发人员中变得流行。那是以前不存在的。感谢你的贡献,@rseanhall

两个主要 PR:

事件管道

事件管道是我们在.NET Core 2.2 中添加的新子系统和 API,可以在任何操作系统上执行性能和其他诊断调查。在.NET 5.0 中,事件管道已得到扩展,以使事件探查器能够写入事件管道事件。对于以前依靠 ETW(在 Windows 上)监视应用程序行为和性能的分析探查器,此方案至关重要。

现在可以通过事件管道获得程序集加载信息。这项改进是开始提供类似的诊断功能(作为.NET Framework 的一部分)的开始,例如Fusion Log Viewer。现在,你可以使用dotnet-trace通过以下命令来收集此信息:

dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4:4 --process-id [process ID]

使用事件管道的跟踪程序集加载中描述了该工作流程。你可以查看简单测试应用程序的程序集加载信息。

跟踪组件-负载

Microsoft.Extensions.Logging

我们对库中的控制台日志提供程序进行了改进。现在,你可以实现一个自定义,以对控制台输出的格式和颜色进行完全控制。格式化程序 API 通过实现(大多数现代终端支持的)转义序列的子集来实现丰富的格式化。控制台记录器可以解析不受支持的终端上的转义序列,使你可以为所有终端编写单个格式化程序。Microsoft.Extensions.LoggingConsoleFormatterVT-100

除了支持自定义格式器外,我们还添加了内置的 JSON 格式器,该格式器向控制台发出结构化 JSON 日志。

转储调试

调试托管代码需要了解托管对象和构造。数据访问组件(DAC)是运行时执行引擎的子集,该引擎具有这些构造的知识,并且可以在没有运行时的情况下访问这些托管对象。现在,可以使用 WinDBG 或 Windows 在 Windows 上分析在 Linux 上收集的.NET Core 进程转储。dotnet dump analyze

我们还添加了对从 macOS 上运行的.NET 进程捕获 ELF 转储的支持。由于 ELF 不是lldbmacOS 上的本机可执行文件(像这样的本地调试器将无法与这些转储一起使用)文件格式,因此我们将其设为选择加入功能。要在 macOS 上支持转储收集,请设置环境变量。可以使用来分析产生的转储。COMPlus_DbgEnableElfDumpOnMacOS=1``dotnet dump analyze

打印环境信息

随着.NET 扩展了对新操作系统和芯片体系结构的支持,人们有时需要一种打印环境信息的方法。我们创建了一个简单的.NET 工具来执行此操作,称为dotnet-runtimeinfo

你可以使用以下命令安装和运行该工具。

dotnet tool install -f dotnet-runtimeinfo
dotnet-runtimeinfo

该工具以以下格式为你的环境生成输出。

[rich@taumarunui ~]$ dotnet-runtimeinfo
**.NET information
Version: 5.0.0
FrameworkDescription: .NET 5.0.0-preview.8
Libraries version: 5.0.0-preview.8
Libraries hash: 977a00fb9d587a6c1292fb3af3992038ddcd3016

**Environment information
OSDescription: Linux 5.8.3-2-MANJARO-ARM #1 SMP Sat Aug 22 21:00:07 CEST 2020
OSVersion: Unix 5.8.3.2
OSArchitecture: Arm64
ProcessorCount: 6

**CGroup info**
cfs_quota_us: -1
memory.limit_in_bytes: 9223372036854771712
memory.usage_in_bytes: 1199210496

运行时和库

在运行时和库中进行了许多改进。

RyuJIT 中代码质量的改进

此版本的 JIT 有很多改进,我在以前的.NET 5.0 预览文章中分享了许多改进。在本文中,我将介绍社区带来的变化。

垃圾收集器

GC 中进行了以下改进。

现在,GC 通过GC.GetGCMemoryInfo方法公开了最新集合的详细信息。该GCMemoryInfo结构提供有关机内存,堆内存和最新的收集,或最近你指定的那种 GC 的收集信息-短暂的,完全阻塞或背景 GC。

使用此新 API 的最可能的用例是日志记录/监视或向装载机平衡器指示应停止旋转机器以请求完整的 GC。它也可以通过减少缓存大小来避免容器的硬限制。

进行了另一个小而有影响的更改,以将昂贵的reset memory操作推迟到低内存情况。我们希望这些政策上的更改可以降低 GC 延迟(以及总体上降低 GC CPU 使用率)。

Windows Arm64

.NET 应用程序现在可以在 Windows Arm64 上本机运行。这是在.NET Core 3.0 中添加的对 Linux Arm64 的支持(对 glibc 和 musl 的支持)。使用.NET 5.0,你可以在 Windows Arm64 设备(例如Surface Pro X)上开发和运行应用程序。你已经可以通过 x86 仿真在 Windows Arm64 上运行.NET Core 和.NET Framework 应用程序。这是可行的,但是本机 Arm64 执行具有更好的性能。

用于 Arm64 的 MSI 安装程序是此版本的最终更改之一。你可以在下图中看到.NET 5.0 SDK 安装程序。

.NET 5.0 SDK Arm64安装程序

.NET 5.0 SDK 当前在 Windows Arm64 上不包含 Windows 桌面组件-Windows 窗体和 WPF。最初在.NET 5.0 Preview 8 帖子中共享了此更改。我们希望在 5.0 服务更新中添加适用于 Windows Arm64 的 Windows 桌面包。我们目前没有可分享的日期。在此之前,Windows Arm64 上支持 SDK,控制台和 ASP.NET Core 应用程序,但 Windows 桌面组件不支持。

Arm64 性能

一年多来,我们一直在大力投资以提高 Arm64 性能。我们致力于通过.NET 使 Arm64 成为高性能平台。这些改进同样适用于 Windows 和 Linux。平台的可移植性和一致性一直是.NET 的引人注目的特征。这包括在任何使用.NET 的地方都提供出色的性能。在.NET Core 3.x 中,Arm64 与 x64 具有同等的功能,但缺少一些关键的性能功能和投资。我们已经在.NET 5.0 中解决了该问题,如.NET 5.0 中的Arm64 Performance 中所述。

改进之处:

  • 调整 Arm64 的 JIT 优化(示例
  • 启用并利用 Arm64 硬件内部函数(示例)。
  • 调整 Arm64 库中对性能至关重要的算法(示例)。

有关更多详细信息,请参见在.NET 5.0 中提高 Arm64 性能

硬件内在属性是我们在.NET Core 3.0 中添加的低级性能功能。当时,我们增加了对 x86-64 指令和芯片的支持。作为.NET 5.0 的一部分,我们正在扩展该功能以支持 Arm64。仅创建内在函数并不能提高性能。它们需要在性能关键的代码中使用。我们在.NET 5.0 的.NET 库中充分利用了Arm64 内在函数。你也可以在自己的代码中执行此操作,尽管你需要熟悉 CPU 指令才能执行此操作。

我将以类比的方式解释硬件内在函数的工作方式。在大多数情况下,开发人员都依赖于.NET 中内置的类型和 API,例如或。这些 API 通常通过P / Invoke功能利用本机操作系统 API 。P / Invoke 支持高性能的本机互操作,并且为此目的在.NET 库中广泛使用。你可以自己使用此功能来调用本机 API。硬件内在函数相似,除了代替调用操作系统 API 之外,它们使你可以直接在代码中使用 CPU 指令。它大致等效于.NET 版本的C ++内部函数string.Split``HttpClient。最好将硬件固有特性视为 CPU 硬件加速功能。它们提供了非常明显的好处,现在已成为.NET 库性能基础的关键部分,并为你可以在.NET 5.0 性能文章中了解的许多好处负责。与 C ++相比,当将.NET 内部函数 AOT 编译为“准备运行”文件时,内部函数不会损害运行时性能。

注意:Visual C ++编译器具有类似的内在功能。你可以直接将 C ++与.NET 硬件内在函数进行比较,就像你_mm_i32gather_epi32System.Runtime.Intrinsics.X86.Avx2x64(amd64)内在函数列表Intel Intrinsics guide 中进行搜索一样。你将看到很多相似之处。

我们将在 5.0 中对 Arm64 性能进行首次重大投资,并且我们将在后续版本中继续进行这项工作。我们直接与Arm Holdings 的工程师合作,确定产品改进的优先级,并设计最能充分利用Armv8 ISA 的算法。其中一些改进将为 Arm32 带来价值,但是,我们并未对 Arm32 进行独特的努力。如果你使用的是 Raspberry Pi,则如果安装新的 Arm64 版本的 Raspberry Pi OS,你将享受这些改进。

我们希望 Apple 随时可以发布新的基于 Apple Silicon 的 Mac 计算机。我们已经拥有用于 Apple Silicon.NET 6.0 的早期版本,并一直在与 Apple 工程师合作,以帮助针对该平台优化.NET。我们还与 Apple Silicon进行了早期社区互动(信誉@snickler)。

P95 +延迟

我们看到越来越多的大型面向 Internet 的站点和服务托管在.NET 上。尽管合理地关注每秒请求数(RPS)指标,但我们发现没有大型站点所有者向我们询问此事或需要数百万 RPS。但是,我们听到了很多有关延迟的信息,特别是关于改善P95 或 P99 延迟的信息。通常,根据实现特定的 P95 指标(而不是 P50)来选择为站点配置的机器或核心的数量(以及最大的成本驱动因素)。我们认为延迟是真正的“金钱指标”。

我们在 Stack Overflow 的朋友在共享服务数据方面做得很好。他们的工程师之一 Nick Craver 最近分享了他们看到的对延迟的改进,这是由于迁移到.NET Core 所致

固定对象一直是 GC 性能的长期挑战,特别是因为它们会加速(或导致)内存碎片。我们为固定对象添加了新的 GC 堆。该固定对象堆是基于这样的假设,有一个过程,但他们的存在会导致不相称的性能挑战极少数固定的对象。将固定的对象(尤其是由.NET 库作为实现细节创建的对象)移动到唯一的区域是有意义的,而世代 GC 堆几乎没有或没有固定的对象,因此具有更高的性能。

最近,我们一直在挑战 GC 中的长期挑战。dotnet / runtime#2795将新方法应用于 GC 静态扫描,从而在确定 GC 堆对象的活动性时避免了锁争用。dotnet / runtime#25986使用一种新算法在垃圾回收的标记阶段平衡内核之间的 GC 工作,这应该增加具有大堆的垃圾回收的吞吐量,从而减少延迟。

改善分层编译性能

我们一直在努力改进多个版本的分层编译。我们仍然将其视为启动和稳态性能的关键性能特征。在此版本中,我们对分层编译进行了两项重大改进。

分层编译的主要机制是调用计数。一旦某个方法被调用了 n 次,运行时就会要求 JIT 以更高的质量重新编译该方法。从我们最早的性能分析中,我们知道呼叫计数机制太慢,但是没有找到解决该问题的直接方法。作为.NET 5.0 的一部分,我们改进了分层 JIT 编译使用的调用计数机制,以平滑启动期间的性能。在过去的发行版中,我们已经看到报告称,在进程生命周期的前 10 到 15 秒钟内,性能会发生不可预测的变化(主要是针对 Web 服务器)。现在应该解决。

我们发现的另一个性能挑战是对具有循环的方法使用分层编译。根本的问题是,你可以使用带有循环多次的循环的冷方法(仅调用一次或几次; $ lt; n)。我们称这种病理情况为“冷方法”。热循环”。可以想象Main应用程序的方法会发生这种情况。结果,默认情况下,我们禁用了带循环方法的分层编译。相反,我们使应用程序可以选择使用带循环的分层编译。在某些情况下看到了个位数的高性能改进后,PowerShell 是选择执行此操作的应用程序。

为了更好地解决循环问题,我们实现了栈上替换(OSR)。这类似于 Java 虚拟机具有的同名功能。OSR允许在方法执行过程中重新编译当前正在运行的方法执行的代码,而那些方法是“堆栈上”活动的。该功能目前处于试验和选择启用状态,并且仅在 x64 上可用。

要使用 OSR,必须启用多个功能。在PowerShell 的项目文件是一个很好的起点。你会注意到分层编译和所有快速向导功能均已启用。此外,你需要将COMPlus_TC_OnStackReplacement环境变量设置为1

另外,你可以设置以下两个环境变量,假设所有其他设置均为默认值:

  • COMPlus_TC_QuickJitForLoops=1
  • COMPlus_TC_OnStackReplacement=1

我们不打算在.NET 5.0 中默认启用 OSR,并且尚未决定是否在生产中支持 OSR。

在 Windows 上支持 ICU

我们将ICU库用于 Unicode 和全球化支持,以前仅在 Linux 和 macOS 上使用。我们现在在 Windows 10 上使用相同的库此更改使全球化 API 的行为(如特定文化区域的字符串比较)在 Windows 10,macOS 和 Linux 之间保持一致。我们还将 ICU 与 Blazor WebAssembly 一起使用。

将 System.DirectoryServices.Protocols 扩展到 Linux 和 macOS

我们一直在添加对System.DirectoryServices.Protocols 的跨平台支持。这包括对 Linux 的支持对 macOS 的支持。Windows 支持已经存在。

System.DirectoryServices.Protocols是比System.DirectoryServices更低级别的 API ,并且启用(或可以用来启用)更多方案。System.DirectoryServices 包含仅 Windows 的概念/实现,因此制作跨平台并不是一个显而易见的选择。这两个 API 集都可以控制和与LDAPActive Directory 之类的目录服务服务器进行交互。

系统文本

System.Text.Json.NET 5.0 中已进行了显着改进,以提高性能,可靠性,并使熟悉Newtonsoft.Json 的人们更容易采用。它还包括对将 JSON 对象反序列化为记录的支持

如果你正在寻找替代方法,则应查阅迁移指南。该指南阐明了这两个 API 之间的关系。旨在涵盖与相同的许多场景,但并不旨在替代流行的 JSON 库或实现与流行的 JSON 库的功能对等。我们试图在性能和可用性之间保持平衡,并在设计选择中偏向性能。System.Text.Json``Newtonsoft.Json``System.Text.Json``Newtonsoft.Json

HttpClient 扩展方法

JsonSerializer现在公开了扩展方法HttpClient并大大简化了将这两个 API 一起使用的过程。这些扩展方法消除了复杂性,并为你解决了各种情况,包括处理内容流和验证内容媒体类型。史蒂夫·戈登(Steve Gordon)出色地解释了使用 HttpClient 和 System.Net.Http.Json 发送和接收 JSON的好处。

以下示例Forecast使用新的将天气预报 JSON 数据反序列化为记录