{"id":37429,"date":"2021-11-08T09:03:44","date_gmt":"2021-11-08T16:03:44","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=37429"},"modified":"2023-06-06T17:50:43","modified_gmt":"2023-06-07T00:50:43","slug":"announcing-net-6","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-6\/","title":{"rendered":"Announcing .NET 6 &#8212; The Fastest .NET Yet"},"content":{"rendered":"<p>Welcome to .NET 6. Today&#8217;s release is the result of just over a <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/whats-new\/dotnet-6\">year&#8217;s worth of effort<\/a> by the .NET Team and community. C# 10 and F# 6 deliver language improvements that make your code simpler and better. There are <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance-improvements-in-net-6\/\">massive gains in performance<\/a>, which we&#8217;ve seen dropping the cost of hosting cloud services at Microsoft. .NET 6 is the first release that natively supports Apple Silicon (Arm64) and has also been improved for Windows Arm64. We built a new dynamic profile-guided optimization (PGO) system that delivers deep optimizations that are only possible at runtime. Cloud diagnostics have been improved with <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-dotnet-monitor\/\">dotnet monitor<\/a> and <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/opentelemetry-net-reaches-v1-0\/\">OpenTelemetry<\/a>. <a href=\"https:\/\/webassembly.org\/\">WebAssembly<\/a> support is more capable and performant. New APIs have been added, for <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/http-3-support-in-dotnet-6\/\">HTTP\/3<\/a>, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/try-the-new-system-text-json-source-generator\/\">processing JSON<\/a>, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/preview-features-in-net-6-generic-math\/\">mathematics<\/a>, and directly manipulating memory. .NET 6 will be <a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/releases.md\">supported for three years<\/a>. Developers have already started upgrading applications to .NET 6 and we&#8217;ve heard great early results in production. .NET 6 is ready for your app.<\/p>\n<p>You can <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/6.0\">download .NET 6<\/a> for Linux, macOS, and Windows.<\/p>\n<ul>\n<li><a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/6.0\">Installers and binaries<\/a><\/li>\n<li><a href=\"https:\/\/hub.docker.com\/_\/microsoft-dotnet\">Container images<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/release-notes\/6.0\/install-linux.md\">Linux packages<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/release-notes\/6.0\/README.md\">Release notes<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/release-notes\/6.0\/preview\/api-diff\/rc1\/README.md\">API diff<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/release-notes\/6.0\/known-issues.md\">Known issues<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/issues\/6881\">GitHub issue tracker<\/a><\/li>\n<\/ul>\n<p>See the <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-asp-net-core-in-net-6\/\">ASP.NET Core<\/a>, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/get-to-know-ef-core-6\/\">Entity Framework<\/a>, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-windows-forms-in-net-6-0\/\">Windows Forms<\/a>, <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/whats-new\/dotnet-6\">.NET MAUI<\/a>, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-yarp-1-0-release\/\">YARP<\/a>,\u00a0 and <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-monitor-in-net-6\/\">dotnet monitor<\/a> posts for what\u2019s new in a variety of scenarios.<\/p>\n<p>Visual Studio 2022 is also releasing today. <a href=\"https:\/\/aka.ms\/vs2022gablog\">Read the announcement<\/a> and <a href=\"https:\/\/visualstudio.microsoft.com\/launch\/\">watch the launch event<\/a> to learn more about the release.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/powershell\/general-availability-of-powershell-7-2\">PowerShell 7.2<\/a> is also releasing today, built on .NET 6. PowerShell users get access to the same performance improvements and APIs as .NET developers.<\/p>\n<p><a href=\"https:\/\/dotnetconf.net\">.NET Conf<\/a> is a free, three-day, virtual developer event that celebrates the major releases of .NET. It starts tomorrow and runs November 9-11 featuring speakers from our team, teams at Microsoft, and the broader community with over 80 sessions. <a href=\"https:\/\/dotnetconf.net\">Tune in to learn and engage with us<\/a>.<\/p>\n<p>Check out the new <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/category\/conversations\/\">conversations posts<\/a> for in-depth engineer-to-engineer discussions on the latest .NET features.<\/p>\n<h2>.NET 6 Highlights<\/h2>\n<p><a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/whats-new\/dotnet-6\">.NET 6 is:<\/a><\/p>\n<ul>\n<li><strong>Production stress-tested<\/strong> with <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/azure-active-directorys-gateway-is-on-net-6-0\/\">Microsoft services<\/a>, <a href=\"https:\/\/twitter.com\/nycdotnet\/status\/1438970921302347778\">cloud apps run by other companies<\/a>, and open <a href=\"https:\/\/github.com\/jellyfin\/jellyfin\/pull\/6806\">source projects<\/a>.<\/li>\n<li><strong>Supported for three years<\/strong> as the latest <a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/releases.md\">long term support (LTS) release<\/a>.<\/li>\n<li><strong>Unified platform<\/strong> across <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/tree\/main\/src\/Components\">browser<\/a>, <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\">cloud<\/a>, <a href=\"https:\/\/github.com\/dotnet\/winforms\">desktop<\/a>, <a href=\"https:\/\/github.com\/dotnet\/iot\/\">IoT<\/a>, and <a href=\"https:\/\/github.com\/dotnet\/maui\">mobile apps<\/a>, all using the same .NET Libraries and the ability to share code easily.<\/li>\n<li><strong>Performance<\/strong> is <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance-improvements-in-net-6\/\">greatly improved across the board<\/a> and for <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/file-io-improvements-in-dotnet-6\/\">file I\/O in particular<\/a>, which together result in decreased execution time, latency, and memory use.<\/li>\n<li><strong>C# 10<\/strong> <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/welcome-to-csharp-10\/\">offers language improvements<\/a> such as record structs, implicit using, and new lambda capabilities, while the compiler adds incremental source generators.\n<strong>F# 6<\/strong> adds new features including <a href=\"https:\/\/aka.ms\/fsharp-6-release\">Task based async, pipeline debugging and numerous performance improvements<\/a>.<\/li>\n<li><strong>Visual Basic<\/strong> has improvements in the <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-for-visual-basic-in-visual-studio-2022\">Visual Studio experience and for Windows Forms project open experience<\/a>.<\/li>\n<li><strong>Hot Reload<\/strong> enables you to skip rebuilding and restarting your app to view a new change &#8212; while your app is running &#8212; supported in Visual Studio 2022 and from the .NET CLI, for C# and Visual Basic.<\/li>\n<li><strong>Cloud diagnostics<\/strong> have been improved with <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/opentelemetry-net-reaches-v1-0\/\">OpenTelemetry<\/a> and <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-dotnet-monitor\/\">dotnet monitor<\/a>, which is now supported in production and available with Azure App Service.<\/li>\n<li><strong>JSON APIs<\/strong> are <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/try-the-new-system-text-json-source-generator\/\">more capable<\/a> and have higher performance with a source generator for the serializer.<\/li>\n<li><strong>Minimal APIs<\/strong> <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/fundamentals\/minimal-apis\">introduced in ASP.NET Core<\/a> to <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/tutorials\/min-web-api?view=aspnetcore-6.0&amp;tabs=visual-studio\">simplify the getting started experience<\/a> and improve the performance of HTTP services.<\/li>\n<li><strong>Blazor<\/strong> <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/components\/?view=aspnetcore-6.0#render-razor-components-from-javascript\">components can now be rendered from JavaScript<\/a> and integrated with existing JavaScript based apps.<\/li>\n<li><strong>WebAssembly AOT<\/strong> <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/host-and-deploy\/webassembly?view=aspnetcore-6.0#ahead-of-time-aot-compilation\">compilation for Blazor WebAssembly (Wasm) apps<\/a>, as well as support for runtime relinking and native dependencies.<\/li>\n<li><strong>Single-page apps<\/strong> built with ASP.NET Core now use a more flexible pattern that can be used with Angular, React, and other popular frontend JavaScript frameworks.<\/li>\n<li><strong>HTTP\/3<\/strong> has been added so that ASP.NET Core, HttpClient, and gRPC can all <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/http-3-support-in-dotnet-6\/\">interact with HTTP\/3 clients and servers<\/a>.<\/li>\n<li><strong>File IO<\/strong> now has support for symbolic links and has greatly improved performance with a re-written-from-scratch <code>FileStream<\/code>.<\/li>\n<li><strong>Security<\/strong> has been improved with support for <a href=\"https:\/\/www.openssl.org\/blog\/blog\/2021\/09\/07\/OpenSSL3.Final\/\">OpenSSL 3<\/a>, the <a href=\"https:\/\/cryptopp.com\/wiki\/ChaCha20Poly1305\">ChaCha20Poly1305 encryption scheme<\/a>, and runtime defense-in-depth mitigations, specifically <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2021\/runtime-security-mitigations.md#wx\">W^X<\/a> and <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2021\/runtime-security-mitigations.md#intel-control-flow-enforcement-technology-cet\">CET<\/a>.<\/li>\n<li><strong>Single-file apps (extraction-free)<\/strong> can be published for Linux, macOS, and Windows (previously only Linux).<\/li>\n<li><strong>IL trimming<\/strong> is now more capable and effective, with new warnings and analyzers to ensure correct final results.<\/li>\n<li><strong>Source generators and analyzers<\/strong> have been added that help you produce better, safer, and higher performance code.<\/li>\n<li><strong>Source build<\/strong> enables organizations like Red Hat to build .NET from source and offer their own builds to their users.<\/li>\n<\/ul>\n<p>The release includes about ten thousand git commits. Even with the length of this post, it skips over many improvements. You&#8217;ll have to download and try .NET 6 to see everything that&#8217;s new.<\/p>\n<h2>Support<\/h2>\n<p>.NET 6 is a Long-term Support (LTS) release that will be <a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/releases.md\">supported for three years<\/a>. It is <a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/release-notes\/6.0\/supported-os.md\">supported on multiple operating systems<\/a>, including macOS Apple Silicon and Windows Arm64.<\/p>\n<p><a href=\"http:\/\/redhatloves.net\/\">Red Hat supports .NET<\/a> on Red Hat Enterprise Linux, <a href=\"https:\/\/twitter.com\/runfaster2000\/status\/1458466083812171780\">in collaboration with the .NET Team<\/a>. On RHEL 8 and later, .NET 6 will be available for the AMD and Intel (x64_64), ARM (aarch64), and IBM Z and LinuxONE (s390x) architectures.<\/p>\n<p>Please start migrating your apps to .NET 6, particularly .NET 5 apps. We have heard from early adopters that upgrading to .NET 6 is straightforward from .NET Core 3.1 and .NET 5.<\/p>\n<p>.NET 6 is supported with <a href=\"https:\/\/visualstudio.microsoft.com\/vs\">Visual Studio 2022<\/a> and <a href=\"https:\/\/visualstudio.microsoft.com\/vs\/mac\/\">Visual Studio 2022 for Mac<\/a>. It is not supported with Visual Studio 2019, Visual Studio for Mac 8, or MSBuild 16. If you want to use .NET 6, you will need to upgrade to <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/msbuild-and-64-bit-visual-studio-2022\/\">Visual Studio 2022 (which is also now 64-bit<\/a>). .NET 6 is supported with the Visual Studio Code <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-dotnettools.csharp\">C# extension<\/a>.<\/p>\n<p>Azure App Service:<\/p>\n<ul>\n<li>Azure Functions <a href=\"https:\/\/go.microsoft.com\/fwlink\/?linkid=2178604\">now supports running serverless functions in .NET 6<\/a>.<\/li>\n<li>The <a href=\"https:\/\/go.microsoft.com\/fwlink\/?linkid=2178304\">App Service .NET 6 GA Announcement<\/a> has information and details for ASP.NET Core developers excited to get going with .NET 6 today.<\/li>\n<li>Azure Static Web Apps now supports <a href=\"https:\/\/go.microsoft.com\/fwlink\/?linkid=2178605\">full-stack .NET 6 applications with Blazor WebAssembly frontends and Azure Function APIs<\/a>.<\/li>\n<\/ul>\n<p>Note: If you&#8217;re app is already running a .NET 6 Preview or RC build on App Service, it will be auto-updated on the first restart once the .NET 6 runtime and SDK are deployed to your region. If you deployed a self-contained app, you will need to re-build and re-deploy.<\/p>\n<h2>Unified and extended platform<\/h2>\n<p>.NET 6 delivers a unified platform, for <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/tree\/main\/src\/Components\">browser<\/a>, <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\">cloud<\/a>, <a href=\"https:\/\/github.com\/dotnet\/winforms\">desktop<\/a>, <a href=\"https:\/\/github.com\/dotnet\/iot\/\">IoT<\/a>, and <a href=\"https:\/\/github.com\/dotnet\/maui\">mobile apps<\/a>. The underlying platform has been updated to serve the needs of all app types and to make it easy to re-use code across all your apps. New capabilities and improvements are available to all apps at the same time, so that your code running in the cloud or on a mobile device behaves the same way and has the same benefits.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2021\/11\/dotnet-unified-platform.png\" alt=\"dotnet-unified-platform\" \/><\/p>\n<p>The reach of .NET developers continues to widen with each release. <a href=\"https:\/\/github.com\/dotnet\/machinelearning\">Machine learning<\/a> and <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/host-and-deploy\/webassembly?view=aspnetcore-6.0#ahead-of-time-aot-compilation\">WebAssembly<\/a> are two of the most recent additions. For example, with machine learning, you can write apps that look for anomalies in streaming data. With WebAssembly, you can <a href=\"http:\/\/blazor.net\/\">host .NET apps in the browser<\/a>, just like HTML and JavaScript, or <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/components\/?view=aspnetcore-6.0#render-razor-components-from-javascript\">mix them with HTML and JavaScript<\/a>.<\/p>\n<p>One of the most exciting additions is <a href=\"https:\/\/github.com\/dotnet\/maui\">.NET Multi-platform App UI (.NET MAUI)<\/a>. You can now write code &#8212; in a single project &#8212; that delivers a modern client app experience across desktop and mobile operating systems. .NET MAUI will be released a little later than .NET 6. We&#8217;ve put a lot of time and effort into .NET MAUI and are very excited to release it and see .NET MAUI apps in production.<\/p>\n<p>Of course, .NET apps are also at home on <a href=\"https:\/\/docs.microsoft.com\/dotnet\/desktop\/\">Windows desktop<\/a> &#8212; with <a href=\"https:\/\/github.com\/dotnet\/winforms\">Windows Forms<\/a> and <a href=\"https:\/\/github.com\/dotnet\/wpf\">WPF<\/a> &#8212; and in the cloud with ASP.NET Core. They are the app types we&#8217;ve offered for the longest and they continue to be very popular, and we&#8217;ve improved them in .NET 6.<\/p>\n<h2>Targeting .NET 6<\/h2>\n<p>Continuing on the theme of a broad platform, writing .NET code across all those operating systems is easy.<\/p>\n<p>To <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2021\/net6.0-tfms\/net6.0-tfms.md\">target .NET 6<\/a>, you need to use a .NET 6 target framework, like the following:<\/p>\n<pre><code class=\"language-xml\">&lt;TargetFramework&gt;net6.0&lt;\/TargetFramework&gt;<\/code><\/pre>\n<p>The <code>net6.0<\/code> Target Framework Moniker (TFM) gives you access to all the cross-platform APIs that .NET offers. This is the best option if you are writing console apps, ASP.NET Core apps, or reusable cross-platform libraries.<\/p>\n<p>If you are targeting a specific operating system (like if writing a <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-windows-forms-in-net-6-0\/\">Windows Forms<\/a> or iOS app), then there is another set of TFMs (that each target a self-evident operating system) for you to use. They give you access to all the APIs in <code>net6.0<\/code> plus a bunch of operating-system-specific ones.<\/p>\n<ul>\n<li><code>net6.0-android<\/code><\/li>\n<li><code>net6.0-ios<\/code><\/li>\n<li><code>net6.0-maccatalyst<\/code><\/li>\n<li><code>net6.0-tvos<\/code><\/li>\n<li><code>net6.0-windows<\/code><\/li>\n<\/ul>\n<p>The version-less TFMs are each equivalent to targeting the lowest supported operating system version by .NET 6. You can specify an operating system version if you want to be specific or to get access to newer APIs.<\/p>\n<p>The <code>net6.0<\/code> and <code>net6.0-windows<\/code> TFMs are supported (same as .NET 5). The Android and Apple TFMs are new with .NET 6 and currently in preview. They will be supported with a later .NET 6 update.<\/p>\n<p>There are no compatibility relationships between the OS-specific TFMs. For example, <code>net6.0-ios<\/code> is not compatible with <code>net6.0-tvos<\/code>. If you want to share code, you need to do that with source with <code>#if<\/code> statements or binaries with <code>net6.0<\/code> targeted code.<\/p>\n<h2>Performance<\/h2>\n<p>The team has had a deep and growing focus on performance ever since we started the .NET Core project. <a href=\"https:\/\/github.com\/stephentoub\">Stephen Toub<\/a> does an amazing job of capturing the progress of .NET performance with each release. If you haven&#8217;t had the chance, I recommend taking a look at his <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance-improvements-in-net-6\/\">Performance improvements in .NET 6<\/a> post.<\/p>\n<p>In this post, I&#8217;ve captured some heavy-hitter performance improvements that you&#8217;ll want to know about, including File IO, interface casting, PGO, and System.Text.Json.<\/p>\n<h3>Dynamic PGO<\/h3>\n<p><strong>Dynamic Profile-guided Optimization (PGO)<\/strong> can markedly improve steady-state performance. For example, PGO gives a 26% improvement (510K -&gt; 640K) in requests per second for the TechEmpower JSON &#8220;MVC&#8221; suite.<\/p>\n<p>Dynamic PGO builds upon <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/tiered-compilation-preview-in-net-core-2-1\/\">Tiered Compilation<\/a>, which enables methods to first be compiled very quickly (referred to as &#8220;Tier 0&#8221;) to improve startup performance, and to then subsequently be recompiled (referred to as &#8220;Tier 1&#8221;) with lots of optimization enabled once that method has shown to be impactful. This model enables methods to be instrumented in Tier 0 to allow various observations to be made about the code&#8217;s execution. When these methods are rejitted at Tier 1, the information gathered from the Tier 0 executions is used to better optimize the Tier 1 code. That&#8217;s the essence of the mechanism.<\/p>\n<p>Dynamic PGO will have slightly slower startup times than the default runtime, as there is extra code running in Tier 0 methods to observe method behavior.<\/p>\n<p>To enable Dynamic PGO, set <code>DOTNET_TieredPGO=1<\/code> in the environment where your application will run. You must also ensure that Tiered Compilation is enabled (it is by default). Dynamic PGO is opt-in because it is a new and impactful technology. We want a release of opt-in use and associated feedback to ensure that it is fully stress-tested. We did the same thing with Tiered Compilation. Dynamic PGO is supported and is already in use in production by at least one very large Microsoft service. We encourage you to try it.<\/p>\n<p>You can see more on dynamic PGO benefits in <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance-improvements-in-net-6\/\">Performance in .NET 6<\/a> post, including the following microbenchmark, which measures the cost of a particular LINQ enumerator.<\/p>\n<pre><code class=\"language-csharp\">private IEnumerator&lt;long&gt; _source = Enumerable.Range(0, long.MaxValue).GetEnumerator();\r\n\r\n[Benchmark]\r\npublic void MoveNext() =&gt; _source.MoveNext();<\/code><\/pre>\n<p>Here&#8217;s the result, with and without dynamic PGO.<\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th style=\"text-align: right;\">Mean<\/th>\n<th style=\"text-align: right;\">Code Size<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>PGO Disabled<\/td>\n<td style=\"text-align: right;\">1.905 ns<\/td>\n<td style=\"text-align: right;\">30 B<\/td>\n<\/tr>\n<tr>\n<td>PGO Enabled<\/td>\n<td style=\"text-align: right;\">0.7071 ns<\/td>\n<td style=\"text-align: right;\">105 B<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>That&#8217;s a pretty big difference, but there is also increased code size, which might surprise some readers. This is the size of assembly code generated by the JIT, not memory allocations (which is a more common focus). There is a good explanation for that from the .NET 6 Performance post.<\/p>\n<p>One optimization common in PGO implementations is \u201chot\/cold splitting\u201d, where sections of a method frequently executed (\u201chot\u201d) are moved close together at the beginning of the method, and sections of a method infrequently executed (\u201ccold\u201d) are moved to the end of the method. That enables better use of instruction caches and minimizes loads of likely-unused code.<\/p>\n<p>As context, interface dispatch is the most expensive call type in .NET. Non-virtual method calls are the fastest, and even faster still are calls that can be eliminated via inlining. In this case, dynamic PGO is providing two (alternative) callsites for <code>MoveNext<\/code>. The first &#8212; the hot one &#8212; is a direct call to <code>Enumerable+RangeIterator.MoveNext<\/code> and the other &#8212; the cold one &#8212; is a virtual interface call via <code>IEnumerator&lt;int&gt;<\/code>. It&#8217;s a huge win if the hot one gets called most of the time.<\/p>\n<p>This is the magic. When the JIT instrumented the Tier 0 code for this method, that included instrumenting this interface dispatch to track the concrete type of <code>_source<\/code> on each invocation. And the JIT found that every invocation was on a type called <code>Enumerable+RangeIterator<\/code>, which is a <a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/d019e70d2b7c2f7cd1137fac084dbcdc3d2e05f5\/src\/libraries\/System.Linq\/src\/System\/Linq\/Range.cs#L31\">private class<\/a> used to implement <code>Enumerable.Range<\/code> inside of the <code>Enumerable<\/code> implementation. As such, for Tier 1 the JIT has emitted a check to see whether the type of <code>_source<\/code> is that <code>Enumerable+RangeIterator<\/code>: if it isn&#8217;t, then it jumps to the cold section we previously highlighted that&#8217;s performing the normal interface dispatch. But if it is &#8212; which based on the profiling data is expected to be the case the vast majority of the time &#8212; it can then proceed to directly invoke the <code>Enumerable+RangeIterator.MoveNext<\/code> method, non-virtualized. Not only that, but it decided it was profitable to inline that <a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/d019e70d2b7c2f7cd1137fac084dbcdc3d2e05f5\/src\/libraries\/System.Linq\/src\/System\/Linq\/Range.cs#L47-L67\"><code>MoveNext<\/code><\/a> method. The net effect is that the generated assembly code is bit larger, but optimized for the exact scenario expected to be most common. Those are the kind of wins we intended when we started building dynamic PGO.<\/p>\n<p>Dynamic PGO is discussed again in the RyuJIT section.<\/p>\n<h3>File IO Improvements<\/h3>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/file-io-improvements-in-dotnet-6\/\"><code>FileStream<\/code> was almost completely re-written in .NET 6<\/a>, with a focus on improving async File IO performance. On Windows, the implementation no longer uses blocking APIs and can be <strong>up to a few times faster<\/strong>! We&#8217;ve also made improvements to memory usage, on all platforms. After the first async operation (which typically allocates), we&#8217;ve made async operations <strong>allocation-free<\/strong>! In addition, we have made the behavior for edge cases uniform where Windows and Unix implementations were different (and it was possible).<\/p>\n<p>The performance improvements of this re-write benefit all operating systems. The benefit to Windows is the highest since it was farther behind. macOS and Linux users should also see significantly <code>FileStream<\/code> performance improvements.<\/p>\n<p>The following benchmark writes 100 MB to a new file.<\/p>\n<pre><code class=\"language-csharp\">private byte[] _bytes = new byte[8_000];\r\n\r\n[Benchmark]\r\npublic async Task Write100MBAsync()\r\n{\r\n    using FileStream fs = new(\"file.txt\", FileMode.Create, FileAccess.Write, FileShare.None, 1, FileOptions.Asynchronous);\r\n    for (int i = 0; i &lt; 100_000_000 \/ 8_000; i++)\r\n        await fs.WriteAsync(_bytes);\r\n}<\/code><\/pre>\n<p>On Windows with an SSD drive, we observed a <strong>4x speedup<\/strong> and more than a <strong>1200x allocation drop<\/strong>:<\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th>Runtime<\/th>\n<th style=\"text-align: right;\">Mean<\/th>\n<th style=\"text-align: right;\">Ratio<\/th>\n<th style=\"text-align: right;\">Allocated<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Write100MBAsync<\/td>\n<td>.NET 5.0<\/td>\n<td style=\"text-align: right;\">1,308.2 ms<\/td>\n<td style=\"text-align: right;\">1.00<\/td>\n<td style=\"text-align: right;\">3,809 KB<\/td>\n<\/tr>\n<tr>\n<td>Write100MBAsync<\/td>\n<td>.NET 6.0<\/td>\n<td style=\"text-align: right;\">306.8 ms<\/td>\n<td style=\"text-align: right;\">0.24<\/td>\n<td style=\"text-align: right;\">3 KB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We also recognized the need for more high-performance file IO features: concurrent reads and writes, and scatter\/gather IO. We introduced new APIs to the <code>System.IO.File<\/code> and <code>System.IO.RandomAccess<\/code> classes for those cases.<\/p>\n<pre><code class=\"language-csharp\">async Task AllOrNothingAsync(string path, IReadOnlyList&lt;ReadOnlyMemory&lt;byte&gt;&gt; buffers)\r\n{\r\n    using SafeFileHandle handle = File.OpenHandle(\r\n        path, FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.Asynchronous,\r\n        preallocationSize: buffers.Sum(buffer =&gt; buffer.Length)); \/\/ hint for the OS to pre-allocate disk space\r\n\r\n    await RandomAccess.WriteAsync(handle, buffers, fileOffset: 0); \/\/ on Linux it's translated to a single sys-call!\r\n}<\/code><\/pre>\n<p>The sample demonstrates:<\/p>\n<ul>\n<li>Opening a file handle using the new <code>File.OpenHandle<\/code> API.<\/li>\n<li>Pre-allocating disk space using the new <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/file-io-improvements-in-dotnet-6\/#preallocation-size\">Preallocation Size<\/a> feature.<\/li>\n<li>Writing to the file using the new <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/file-io-improvements-in-dotnet-6\/#scatter-gather-io\">Scatter\/Gather IO<\/a> API.<\/li>\n<\/ul>\n<p>The Preallocation Size feature improves performance since write operations don\u2019t need to extend the file and it\u2019s less likely that the file is going to be fragmented. This approach improves reliability since write operations will no longer fail due to running out of space since the space has already been reserved. The Scatter\/Gather IO API reduces the number of sys-calls required to write the data.<\/p>\n<h3>Faster interface checking and casting<\/h3>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/49257\">Interface casting performance has been boosted<\/a> by 16% &#8211; 38%. This improvement is particularly useful for C#&#8217;s pattern matching to and between interfaces.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/1142958\/112411698-3f413600-8d15-11eb-899f-5a0450524b08.png\" alt=\"image\" \/><\/p>\n<p>This chart demonstrates the scale of the improvement for a representative benchmark.<\/p>\n<p>One of the biggest advantages of moving parts of the .NET runtime from C++ to managed C# is that it lowers the barrier to contribution. This includes interface casting, which was moved to C# as an early .NET 6 change. Many more people in the .NET ecosystem are literate in C# than C++ (and the runtime uses challenging C++ patterns). Just being able to read some of the code that composes the runtime is a major step to developing confidence in contributing in its various forms.<\/p>\n<p>Credit to <a href=\"https:\/\/github.com\/benaadams\">Ben Adams<\/a>.<\/p>\n<h3>System.Text.Json Source Generators<\/h3>\n<p>We added a <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/try-the-new-system-text-json-source-generator\/\">source generator for System.Text.Json<\/a> that avoids the need for reflection and code generation at runtime, and that enables generating optimal serialization code at build time. Serializers are typically written with very conservative techniques because they have to be. However, if you read your own serialization source code (that uses a serializer), you can see what the obvious choices should be that can make a serializer a lot more optimal in your specific case. That&#8217;s exactly what this new source generator does.<\/p>\n<p>In addition to increasing performance and reducing memory, the source generator produces code that is optimal for assembly trimming. That can help with producing smaller apps.<\/p>\n<p>Serializing <a href=\"https:\/\/en.wikipedia.org\/wiki\/Plain_old_CLR_object\">POCOs<\/a> is a very common scenario. Using the new source generator, we observe that serialization is <strong>~1.6x faster<\/strong> with <a href=\"https:\/\/gist.github.com\/layomia\/2c236f74b250ed06e0cac5435f78e2cc\">our benchmark<\/a>.<\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th style=\"text-align: right;\">Mean<\/th>\n<th style=\"text-align: right;\">StdDev<\/th>\n<th style=\"text-align: right;\">Ratio<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Serializer<\/td>\n<td style=\"text-align: right;\">243.1 ns<\/td>\n<td style=\"text-align: right;\">9.54 ns<\/td>\n<td style=\"text-align: right;\">1.00<\/td>\n<\/tr>\n<tr>\n<td>SrcGenSerializer<\/td>\n<td style=\"text-align: right;\">149.3 ns<\/td>\n<td style=\"text-align: right;\">1.91 ns<\/td>\n<td style=\"text-align: right;\">0.62<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The <a href=\"https:\/\/www.techempower.com\/benchmarks\/#section=data-r20&amp;hw=ph&amp;test=cached-query\">TechEmpower caching benchmark<\/a> exercises a platform or framework&#8217;s in-memory caching of information sourced from a database. The .NET implementation of the benchmark performs JSON serialization of the cached data in order to send it as a response to the test harness.<\/p>\n<table>\n<thead>\n<tr>\n<th><\/th>\n<th>Requests\/sec<\/th>\n<th>Requests<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>net5.0<\/td>\n<td>243,000<\/td>\n<td>3,669,151<\/td>\n<\/tr>\n<tr>\n<td>net6.0<\/td>\n<td>260,928<\/td>\n<td>3,939,804<\/td>\n<\/tr>\n<tr>\n<td>net6.0 + JSON source gen<\/td>\n<td>364,224<\/td>\n<td>5,499,468<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We <a href=\"https:\/\/github.com\/aspnet\/Benchmarks\/pull\/1683\">observe<\/a> an ~100K RPS gain (<strong>~40% increase<\/strong>). .NET 6 scores a 50% higher throughput than .NET 5 when combined with the <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/46970\">MemoryCache performance improvements<\/a>!<\/p>\n<h2>C# 10<\/h2>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/welcome-to-csharp-10\/\">Welcome to C# 10<\/a>. A major theme of C# 10 is continuing the simplification journey that started with <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/c-9-0-on-the-record\/#top-level-programs\">top-level statements in C# 9<\/a>. The new features remove even more ceremony from <code>Program.cs<\/code>, resulting in programs as short as a single line. They were inspired by talking to people &#8212; students, professional developers, and others &#8212; with no prior C# experience and learning what works best and is intuitive for them.<\/p>\n<p>Most of the <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-6-release-candidate-2\/#net-sdk-c-project-templates-modernized\">.NET SDK templates have been updated<\/a> to deliver the much simpler and more terse experience that is now possible with C# 10. We&#8217;ve heard feedback that some folks don&#8217;t like the new templates because they are not intended for experts, remove object orientation, remove concepts that are important to learn on day one of writing C#, or encourage writing a whole program in one file. Objectively, none of these points are true. The new model is equally intended and equally appropriate for students as professional developers. It is, however, different from the C-derived model we&#8217;ve had until .NET 6.<\/p>\n<p>There are several other features and improvements in C# 10, including record structs.<\/p>\n<h3>Global using directives<\/h3>\n<p>Global using directives let you specify a <code>using<\/code> directive just once and have it applied to every file that you compile.<\/p>\n<p>The following examples show the breadth of the syntax:<\/p>\n<ul>\n<li><code>global using System;<\/code><\/li>\n<li><code>global using static System.Console;<\/code><\/li>\n<li><code>global using Env = System.Environment;<\/code><\/li>\n<\/ul>\n<p>You can put <code>global using<\/code> statements in any <code>.cs<\/code> file, including in <code>Program.cs<\/code>.<\/p>\n<p>Implicit usings is an MSBuild concept that automatically adds a set of <code>global using<\/code> directives <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/sdk\/6.0\/implicit-namespaces-rc1#new-behavior\">depending on the SDK<\/a>. For example, console app implicit usings differ from ASP.NET Core.<\/p>\n<p>Implicit usings are opt-in, and enabled in a <code>PropertyGroup<\/code>:<\/p>\n<ul>\n<li><code>&lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;<\/code><\/li>\n<\/ul>\n<p>Implicit usings are opt-in for existing projects but included by default for new C# projects. For more information, see <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/project-sdk\/overview#implicit-using-directives\">Implicit usings<\/a>.<\/p>\n<h3>File-scoped namespaces<\/h3>\n<p>File-scoped namespaces enable you to declare the namespace for a whole file without nesting the remaining contents in <code>{ ... }<\/code>. Only one is allowed, and it must come before any types are declared.<\/p>\n<p>The new syntax is a single line:<\/p>\n<pre><code class=\"language-csharp\">namespace MyNamespace;\r\n\r\nclass MyClass { ... } \/\/ Not indented<\/code><\/pre>\n<p>This new syntax is an alternative to the three-lined indented style:<\/p>\n<pre><code class=\"language-csharp\">namespace MyNamespace\r\n{\r\n    class MyClass { ... } \/\/ Everything is indented\r\n}<\/code><\/pre>\n<p>The benefit is a reduction indentation in the extremely common case where your whole file is in the same namespace.<\/p>\n<h3>Record structs<\/h3>\n<p>C# 9 introduced records as a special value-oriented form of classes. In C# 10 you can also declare records that are structs. Structs in C# <em>already<\/em> have value equality, but record structs add an <code>==<\/code> operator and an implementation of <code>IEquatable&lt;T&gt;<\/code>, as well as a value-based <code>ToString<\/code> implementation:<\/p>\n<pre><code class=\"language-c#\">public record struct Person\r\n{\r\n    public string FirstName { get; init; }\r\n    public string LastName { get; init; }\r\n}<\/code><\/pre>\n<p>Just like record classes, record structs can be &#8220;positional&#8221;, meaning that they have a primary constructor which implicitly declares public members corresponding to the parameters:<\/p>\n<pre><code class=\"language-c#\">public record struct Person(string FirstName, string LastName);<\/code><\/pre>\n<p>However, unlike record classes, the implicit public members are <em>mutable auto-implemented properties<\/em>. This is so that record structs are a natural grow-up story for tuples. For example, if you have a return type that is <code>(string FirstName, string LastName)<\/code> and you want to grow that up to a named type, you can easily declare the corresponding positional struct record and maintain the mutable semantics.<\/p>\n<p>If you want an immutable record with readonly properties, you can declare the whole record struct <code>readonly<\/code> (just as you can other structs):<\/p>\n<pre><code class=\"language-c#\">public readonly record struct Person(string FirstName, string LastName);<\/code><\/pre>\n<p>C# 10 also supports <code>with<\/code> expressions not just for record structs but for <em>all<\/em> structs, as well as for anonymous types:<\/p>\n<pre><code class=\"language-c#\">var updatedPerson = person with { FirstName = \"Mary\" };<\/code><\/pre>\n<h2>F# 6<\/h2>\n<p><a href=\"https:\/\/aka.ms\/fsharp-6-release\">F# 6<\/a> is about making F# simpler and more performant. This applies to the language design, library, and tooling. Our goal with F# 6 (and beyond) was to remove corner-cases in the language that surprise users or present hurdles to learning F#. We are very pleased to have worked with the F# community in this ongoing effort.<\/p>\n<h3>Making F# faster and more interoperable<\/h3>\n<p>The new <code>task {\u2026}<\/code> syntax directly creates a task and starts it. This is one of the most significant features in F# 6, making asynchronous tasks simpler, more performant and more interoperable with C# and other .NET languages. Previously, creating .NET tasks required using <code>async {\u2026}<\/code> to create a task and invoking <code>Async.StartImmediateAsTask<\/code>.<\/p>\n<p>The <code>task {\u2026}<\/code> feature is built on a foundation called <a href=\"https:\/\/github.com\/fsharp\/fslang-design\/discussions\/455\">\u201cresumable code\u201d RFC FS-1087<\/a>. Resumable code is a core feature, and we expect to use it to build other high-performance asynchronous and yielding state machines in the future.<\/p>\n<p>F# 6 also adds other performance features for library authors including <code>InlineIfLambda<\/code> and unboxed representations for F# active patterns. A particularly significant performance improvement is in the compilation of list and array expressions, which are now up to <strong>4x faster<\/strong> and have better and simpler debugging as well.<\/p>\n<h3>Making F# easier to learn and more uniform<\/h3>\n<p>F# 6 enables the <code>expr[idx]<\/code> indexing syntax. Up until now, F# has used expr.[idx] for indexing. Dropping the dot-notation is based on repeated feedback from first-time F# users, that the use of dot comes across as an unnecessary divergence from the standard practice they expect. In new code, we recommend the systematic use of the new <code>expr[idx]<\/code> indexing syntax. We should all switch to this syntax as a community.<\/p>\n<p>The F# community has contributed key improvements to make the F# language more uniform in F# 6. The most important of these is removing a number of inconsistencies and limitations in F#\u2019s indentation rules. Other design additions to make F# more uniform include the addition of <code>as<\/code> patterns; allowing &#8220;overloaded custom operations&#8221; in computation expression (useful for DSLs); allowing <code>_<\/code> discards on <code>use<\/code> bindings and allowing <code>%B<\/code> for binary formatting in output. The F# core library adds new functions for copy-and-update on lists, arrays, and sequences, plus additional <code>NativePtr<\/code> intrinsics. Some legacy features of F# deprecated since 2.0 now result in errors. Many of these changes better align F# with your expectations, resulting in fewer surprises.<\/p>\n<p>F# 6 also added support for additional \u201cimplicit\u201d and \u201ctype-directed\u201d conversions in F#. This means fewer explicit upcasts, and adds first-class support for .NET-style implicit conversions. F# is also adjusted to be better suited to the era of numeric libraries using 64-bit integers, with implicit widening for 32-bit integers.<\/p>\n<h3>Improving the F# tooling<\/h3>\n<p>Tooling improvements in F# 6 make day to day coding easier. New &#8220;pipeline debugging&#8221; allows you to step, set breakpoints and inspect intermediate values for the F# piping syntax <code>input |&gt; f1 |&gt; f2<\/code>. The debug display of shadowed values has been improved, eliminating a common source of confusion when debugging. F# tooling is now also more performant with the F# compiler performing the parsing stage in parallel. F# IDE tooling is also improved. F# scripting is now even more robust, allowing you to pin the version of the .NET SDK used through <code>global.json<\/code> files.<\/p>\n<h2>Hot Reload<\/h2>\n<p>Hot Reload is another performance feature, focused on developer productivity. It enables you to make a wide variety of code edits to a running application, collapsing the time you need to spend waiting for apps to rebuild, restart, or to re-navigate to the same spot where you were after making a code change.<\/p>\n<p>Hot Reload is available through both the <code>dotnet watch<\/code> CLI tool and Visual Studio 2022. You can use Hot Reload with a large variety of app types such as ASP.NET Core, Blazor, .NET MAUI, Console, Windows Forms (WinForms), WPF, WinUI 3, Azure Functions, and others.<\/p>\n<p>When using the CLI, simply start your .NET 6 app using <code>dotnet watch<\/code>, make any supported edit, and when saving the file (like in Visual Studio Code) those changes will be immediately applied. If the changes are not supported, the details will be logged to the command window.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2021\/11\/hot-reload-cli.png\" alt=\"Hot Reload CLI experience\" \/><\/p>\n<p>This image displays an MVC app being launched with <code>dotnet watch<\/code>. I made edits to both <code>.cs<\/code> and <code>.cshtml<\/code> files (as reported in the log) and both were applied to the code and reflected in the browser very quickly, in less than half a second.<\/p>\n<p>When using Visual Studio 2022, simply start your app, make a <a href=\"https:\/\/docs.microsoft.com\/visualstudio\/debugger\/supported-code-changes-csharp\">supported change<\/a>, and use the new &#8220;Hot Reload&#8221; button (displayed in the following image) to apply those changes. You can also opt to apply changes on save through the drop-down menu on the same button. When using Visual Studio 2022, Hot Reload is available for multiple .NET versions, for .NET 5+, .NET Core, and .NET Framework. For example, you will be able to make code-behind changes to an <code>OnClickEvent<\/code> handler for a button. It is not supported for the <code>Main<\/code> method of an application.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2021\/11\/hot-reload-visual-studio-2022.png\" alt=\"Hot Reload Visual Studio 2022 experience\" \/><\/p>\n<p>Note: There is a <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/45812#issuecomment-961399793\">bug in RuntimeInformation.FrameworkDescription<\/a> that is demonstrated in that image that will be fixed shortly.<\/p>\n<p>Hot Reload also works in tandem with the existing Edit and Continue capability (when stopped at a breakpoint), and XAML Hot Reload for editing an apps UI in real-time. It is currently supported for C# and Visual Basic apps (not F#).<\/p>\n<h2>Security<\/h2>\n<p>Security has been significantly improved in .NET 6. It is always an important focus for the team, including threat modeling, cryptography, and defense in depth mitigations.<\/p>\n<p>On Linux, we rely on <a href=\"https:\/\/www.openssl.org\">OpenSSL<\/a> for all cryptographic operations, including for TLS (required for HTTPS). On macOS and Windows, we rely on OS-provided functionality for the same purpose. With each new version of .NET, we often need to add support for a new build of OpenSSL. .NET 6 adds support for <a href=\"https:\/\/www.openssl.org\/blog\/blog\/2021\/09\/07\/OpenSSL3.Final\/\">OpenSSL 3<\/a>.<\/p>\n<p>The biggest changes with OpenSSL 3 are an improved <a href=\"https:\/\/en.wikipedia.org\/wiki\/FIPS_140-2\">FIPS 140-2<\/a> module and simpler licensing.<\/p>\n<p>.NET 6 requires OpenSSL 1.1 or higher and will prefer the highest installed version of OpenSSL it can find, up to and including v3. In the general case, you&#8217;re most likely to start using OpenSSL 3 when the Linux distribution you use switches to it as the default. Most distros have not yet done that. For example, if you install .NET 6 on Red Hat 8 or Ubuntu 20.04, you will not (at the time of writing) start using OpenSSL 3.<\/p>\n<p>OpenSSL 3, Windows 10 21H1, and Windows Server 2022 all support <a href=\"https:\/\/cryptopp.com\/wiki\/ChaCha20Poly1305\">ChaCha20Poly1305<\/a>. You can <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/45130\">use this new authenticated encryption scheme with .NET 6<\/a> (assuming your environment supports it).<\/p>\n<p>Credit to <a href=\"https:\/\/github.com\/vcsjones\">Kevin Jones<\/a> for Linux support for ChaCha20Poly1305.<\/p>\n<p>We also published a new <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2021\/runtime-security-mitigations.md\">runtime security mitigation roadmap<\/a>. It is important that the runtime you use is safe from textbook attack types. We&#8217;re delivering on that need. In .NET 6, we built initial implementations of <a href=\"https:\/\/en.wikipedia.org\/wiki\/W%5EX\">W^X<\/a> and <a href=\"https:\/\/en.wikipedia.org\/wiki\/Control-flow_integrity\">Intel Control-flow enforcement technology (CET)<\/a>. W^X is fully supported, enabled by default for macOS Arm64, and is opt-in for other environments. CET is opt-in and a preview for all environments. We expect both technologies to be enabled by default for all environments in .NET 7.<\/p>\n<h2>Arm64<\/h2>\n<p>There is a lot of excitement about Arm64 these days, for laptops, cloud hardware, and <a href=\"https:\/\/www.raspberrypi.com\/products\/raspberry-pi-zero-2-w\/\">other devices<\/a>. We feel that same excitement on the .NET team and are doing our best to keep up with that industry trend. We partner directly with engineers at Arm Holdings, Apple, and Microsoft to ensure that our implementations are correct and optimized, and that our plans align. These close partnerships have helped us a lot.<\/p>\n<ul>\n<li>Special thanks to Apple who sent our team a bushel of Arm64 dev kits to work with prior to the M1 chip launching, and for significant technical support.<\/li>\n<li>Special thanks to Arm Holdings, whose engineers code reviewed our Arm64 changes and also made performance improvements.<\/li>\n<\/ul>\n<p>We added initial support for Arm64 with .NET Core 3.0 and Arm32 before that. The team has made major investments in Arm64 in each of the last few releases, and this will continue for the foreseeable future. In .NET 6, our primary focus was on supporting the new Apple Silicon chips and the <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2021\/x64-emulation-on-arm64\/x64-emulation.md\">x64 emulation scenario<\/a> on both macOS and Windows Arm64 OSes.<\/p>\n<p>You can install both the Arm64 and x64 versions of .NET on macOS 11+ and Windows 11+ Arm64 OSes. We had to make several design choices and product changes to make sure that worked.<\/p>\n<p>Our strategy is &#8220;pro native architecture&#8221;. We recommend that you always use the SDK that matches the native architecture, which is the Arm64 SDK on macOS and Windows Arm64. The SDK is large body of software. It is going to be much higher performance running natively on an Arm64 chip than emulated. We&#8217;ve updated the CLI to make that easy. We&#8217;re never going to be focused on optimizing emulated x64.<\/p>\n<p>By default, if you <code>dotnet run<\/code> a .NET 6 app with the Arm64 SDK, it will run as Arm64. You can easily switch to running as x64 with the <code>-a<\/code> argument, like <code>dotnet run -a x64<\/code>. The same argument works for other CLI verbs. See <a href=\"https:\/\/github.com\/dotnet\/sdk\/issues\/21686\">.NET 6 RC2 Update for macOS and Windows Arm64<\/a> for more information.<\/p>\n<p>There&#8217;s a subtlety there that I want to ensure is covered. When you use <code>-a x64<\/code>, the SDK is still running natively as Arm64. There are fixed points in the .NET SDK architecture where process boundaries exist. For the most part, a process must be all Arm64 or all x64. I&#8217;m simplifying a bit, but the .NET CLI waits for the last process creation in the SDK architecture and launches that one as the chip architecture you requested, like x64. That&#8217;s the process your code runs in. That way, you get the benefit of Arm64 as a developer, but your code gets to run in the process it needs. This is only relevant if you need to run some code as x64. If you don&#8217;t, then you can just run everything as Arm64 all the time, and that&#8217;s great.<\/p>\n<h3>Arm64 Support<\/h3>\n<p>The following are the key points you need to know, for macOS and Windows Arm64:<\/p>\n<ul>\n<li>.NET 6 Arm64 and x64 SDKs are supported and recommended.<\/li>\n<li>All in-support Arm64 and x64 runtimes are supported.<\/li>\n<li>.NET Core 3.1 and .NET 5 SDKs work but provide less capability and in some cases are not fully supported.<\/li>\n<li><code>dotnet test<\/code> doesn&#8217;t yet work correctly with x64 emulation. We are <a href=\"https:\/\/github.com\/dotnet\/sdk\/issues\/21391\">working on that<\/a>. <code>dotnet test<\/code> will be improved as part of the 6.0.200 release, and possibly earlier.<\/li>\n<\/ul>\n<p>See <a href=\"https:\/\/github.com\/dotnet\/sdk\/issues\/22380\">.NET Support for macOS and Windows Arm64<\/a> for more complete information.<\/p>\n<p>Linux is missing from this discussion. It doesn&#8217;t support x64 emulation in the same way as macOS and Windows. As a result, these new CLI features and the support approach don&#8217;t directly apply to Linux, nor does Linux need them.<\/p>\n<h3>Windows Arm64<\/h3>\n<p>We have a simple <a href=\"https:\/\/www.nuget.org\/packages\/dotnet-runtimeinfo\/\">tool that demonstrates the environment<\/a> that .NET is running on.<\/p>\n<pre><code class=\"language-bash\">C:Usersrich&gt;dotnet tool install -g dotnet-runtimeinfo\r\nYou can invoke the tool using the following command: dotnet-runtimeinfo\r\nTool 'dotnet-runtimeinfo' (version '1.0.5') was successfully installed.\r\n\r\nC:Usersrich&gt;dotnet runtimeinfo\r\n         42\r\n         42              ,d                             ,d\r\n         42              42                             42\r\n ,adPPYb,42  ,adPPYba, MM42MMM 8b,dPPYba,   ,adPPYba, MM42MMM\r\na8\"    `Y42 a8\"     \"8a  42    42P'   `\"8a a8P_____42   42\r\n8b       42 8b       d8  42    42       42 8PP\"\"\"\"\"\"\"   42\r\n\"8a,   ,d42 \"8a,   ,a8\"  42,   42       42 \"8b,   ,aa   42,\r\n `\"8bbdP\"Y8  `\"YbbdP\"'   \"Y428 42       42  `\"Ybbd8\"'   \"Y428\r\n\r\n**.NET information\r\nVersion: 6.0.0\r\nFrameworkDescription: .NET 6.0.0-rtm.21522.10\r\nLibraries version: 6.0.0-rtm.21522.10\r\nLibraries hash: 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6\r\n\r\n**Environment information\r\nProcessorCount: 8\r\nOSArchitecture: Arm64\r\nOSDescription: Microsoft Windows 10.0.22494\r\nOSVersion: Microsoft Windows NT 10.0.22494.0<\/code><\/pre>\n<p>As you can see, the tool is running natively on Windows Arm64. I&#8217;ll show you what that looks like ASP.NET Core.<\/p>\n<p><img decoding=\"async\" class=\"alignnone\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2021\/11\/dotnet-mvc-windows-arm64.png\" alt=\".NET 6 ASP.NET Core running on Windows Arm64, Announcing .NET 6 \u2014 The Fastest .NET Yet\" width=\"1148\" height=\"637\" \/><\/p>\n<h3>macOS Arm64<\/h3>\n<p>And you can see that the experience is similar on macOS Arm64, with architecture targeting also demonstrated.<\/p>\n<pre><code class=\"language-bash\">rich@MacBook-Air app % dotnet --version\r\n6.0.100\r\nrich@MacBook-Air app % dotnet --info | grep RID\r\n RID:         osx-arm64\r\nrich@MacBook-Air app % cat Program.cs \r\nusing System.Runtime.InteropServices;\r\nusing static System.Console;\r\n\r\nWriteLine($\"Hello, {RuntimeInformation.OSArchitecture} from {RuntimeInformation.FrameworkDescription}!\");\r\nrich@MacBook-Air app % dotnet run\r\nHello, Arm64 from .NET 6.0.0-rtm.21522.10!\r\nrich@MacBook-Air app % dotnet run -a x64\r\nHello, X64 from .NET 6.0.0-rtm.21522.10!\r\nrich@MacBook-Air app % <\/code><\/pre>\n<p>This image demonstrates that Arm64 execution is the default with the Arm64 SDK and how easy it is to switch between targeting Arm64 and x64, using the <code>-a<\/code> argument. The exact same experience works on Windows Arm64.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2021\/11\/macos-arm64-x64-web.png\" alt=\"x64 emulation on macOS Arm64 with ASP.NET Core\" \/><\/p>\n<p>This image demonstrates the same thing, but with ASP.NET Core. I&#8217;m using the same .NET 6 Arm64 SDK as you saw in the previous image.<\/p>\n<h3>Docker on Arm64<\/h3>\n<p>Docker supports containers running with native architecture and in emulation, with native architecture being the default. This seems obvious but can be <a href=\"https:\/\/twitter.com\/runfaster2000\/status\/1403503484226347012\">confusing<\/a> when most of the Docker Hub catalog is x64 oriented. You can use <code>--platform linux\/amd64<\/code> to request an x64 image.<\/p>\n<p>We only support running Linux Arm64 .NET container images on Arm64 OSes. This is because we&#8217;ve never supported running .NET in <a href=\"https:\/\/www.qemu.org\/\">QEMU<\/a>, which is what Docker uses for architecture emulation. It appears that this may be <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/13648#issuecomment-962550359\">due to a limitation in QEMU<\/a>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2021\/11\/docker-arm64.png\" alt=\"Docker on macOS Arm64\" \/><\/p>\n<p>This image demonstrates the console sample we maintain: <code>mcr.microsoft.com\/dotnet\/samples<\/code>. It&#8217;s a fun sample since it contains some basic logic for printing CPU and memory limit information that you can play with. The image I&#8217;ve show sets CPU and memory limits.<\/p>\n<p>Try it for yourself: <code>docker run --rm mcr.microsoft.com\/dotnet\/samples<\/code><\/p>\n<h3>Arm64 Performance<\/h3>\n<p>The Apple Silicon and x64 emulation support projects were super important, however, we also improved Arm64 performance generally.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/63486087\/106191399-ac927980-615f-11eb-83cf-a7647c342618.png\" alt=\"Arm64 performance improvement\" \/><\/p>\n<p>This image demonstrates an improvement in <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/46609\">zeroing out the contents of stack frames<\/a>, which is a common operation. The green line is the new behavior, while the orange line is another (less beneficial) experiment, both of which improve relative to the baseline, represented by the blue line. For this test, lower is better.<\/p>\n<h2>Containers<\/h2>\n<p>.NET 6 is better for containers, primarily based on all the improvements discussed in this post, for both Arm64 and x64. We also made key changes that will help a variety of scenarios. <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/53149\">Validate container improvements with .NET 6<\/a> demonstrates some of these improvements being tested together.<\/p>\n<p>The Windows container improvements and the new environment variable have also been included in the November .NET Framework 4.8 container update, releasing November 9th (tomorrow).<\/p>\n<p>Release notes are available at our docker repositories:<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/dotnet-docker\/issues\/3276\">.NET 6 Container Release Notes<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/microsoft\/dotnet-framework-docker\/issues\/849\">.NET Framework 4.8 November 2021 Container Release Notes<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3>Windows Containers<\/h3>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/46889\">.NET 6 adds support for Windows process-isolated containers<\/a>. If you use <a href=\"https:\/\/docs.microsoft.com\/azure\/aks\/windows-container-cli\">Windows containers in Azure Kubernetes Service (AKS)<\/a>, then you are relying on process-isolated containers. Process-isolated containers can be thought of as very similar to Linux containers. Linux containers use <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/34334\">cgroups<\/a> and Windows process-isolated containers use <a href=\"https:\/\/docs.microsoft.com\/windows\/win32\/procthread\/job-objects\">Job Objects<\/a>. Windows also offer Hyper-V containers, which offers greater isolation through greater virtualization. There are no changes in .NET 6 for Hyper-V containers.<\/p>\n<p>The primary value of this change is that <code>Environment.ProcessorCount<\/code> will now report the correct value with Windows process-isolated containers. If you create a 2-core container on a 64-core machine, <code>Environment.ProcessorCount<\/code> will return <code>2<\/code>. In prior versions, this property would report the total number of processors on a machine, independent of the limit specified by the Docker CLI, Kubernetes, or other container orchestrator\/runtime. This value is used by various parts of .NET for scaling purposes, including the .NET garbage collector (although it relies on a related, lower-level, API). Community libraries also rely on this API for scaling.<\/p>\n<p>We recently validated this new capability with a customer, on Windows Containers in production on AKS using a large set of pods. They were able to run successfully with 50% memory (compared to their typical configuration), a level that previously resulted in <code>OutOfMemoryException<\/code> and <code>StackOverflowException<\/code> exceptions. They didn&#8217;t take the time to find the minimum memory configuration, but we guessed it was significantly below 50% of their typical memory configuration. As a result of this change, they are going to move to cheaper Azure configurations, saving them money. That&#8217;s a nice, easy win, simply by upgrading.<\/p>\n<h3>Optimizing scaling<\/h3>\n<p>We have heard from users that some applications cannot achieve optimal scaling when <code>Environment.ProcessorCount<\/code> reports the correct value. If this sounds like the opposite of what you just read for Windows Containers, it kinda-sorta is. .NET 6 now offers the <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/48094\">DOTNET_PROCESSOR_COUNT environment variable<\/a> to control the value of <code>Environment.ProcessorCount<\/code> manually. In the typical use case, an application might be configured with 4 cores on a 64-core machine, and scale best in terms of 8 or 16 cores. This environment variable can be used to enable that scaling.<\/p>\n<p>This model might seem strange, where <code>Environment.ProcessorCount<\/code> and <code>--cpus<\/code> (via the Docker CLI) values can differ. Container runtimes, by default, are oriented in terms of core equivalents, not actual cores. That means, when you say you want 4 cores, you get the equivalent CPU time of 4 cores, but your app might (in theory) run on many more cores, even all 64 cores on a 64-core machine for a short period. That may enable your app to scale better on more than 4 threads (continuing with the example), and allocating more could be beneficial. This assumes that the thread allocation is based on the value of <code>Environment.ProcessorCount<\/code>. If you opt to set a higher value, it is likely that your app will use more memory. For some workloads, that is an easy tradeoff. At the very least, it is a new option you can test.<\/p>\n<p>This new feature is supported for both Linux and Windows Containers.<\/p>\n<p>Docker also offers a CPU groups feature, where your app is affinitized to specific cores. This feature is not recommended in that scenario since the number of cores an app has access to is concretely defined. We have also seen some issues with using it with Hyper-V containers, and it isn&#8217;t really intended for that isolation mode.<\/p>\n<h3>Debian 11 &#8220;bullseye&#8221;<\/h3>\n<p>We watch <a href=\"https:\/\/github.com\/dotnet\/core\/labels\/os-support\">Linux distros lifecycle and release plans<\/a> very closely and try to make the best choices on your behalf. Debian is the Linux distribution we use for our default Linux images. If you pull the <code>6.0<\/code> tag from one of our container repos, you will pull a Debian image (assuming you are using Linux containers). With each new .NET release, we consider whether we should adopt a new Debian version.<\/p>\n<p>As a matter of policy, we do not change the Debian version for our convenience tags, like <code>6.0<\/code>, mid-release. If we did, some apps would be certain to break. That means, that choosing the Debian version at the start of the release is very important. Also, these images get a lot of use, mostly because they are references by the &#8220;good tags&#8221;.<\/p>\n<p>The Debian and .NET releases are naturally not planned together. When we started .NET 6, we saw that Debian &#8220;bullseye&#8221; would likely be released in 2021. We decided to take a <a href=\"https:\/\/github.com\/dotnet\/dotnet-docker\/pull\/2582\">bet on bullseye from the start of the release<\/a>. We started releasing bullseye-based container images with <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-6-preview-1\/#containers\">.NET 6 Preview 1<\/a> and decided not to look back. The bet was that the .NET 6 release would lose the race with the bullseye release. By August 8th, we still didn&#8217;t know when bullseye would ship, leaving three months before our own release would go out, on November 8th. We didn&#8217;t want to ship a production .NET 6 on a preview Linux, but we held firm late to the plan that we&#8217;d lose this race.<\/p>\n<p>We were pleasantly surprised when <a href=\"https:\/\/www.debian.org\/News\/2021\/20210814\">Debian 11 &#8220;bullseye&#8221;<\/a> was released on August 14th. We lost the race but won the bet. That means that .NET 6 users get the best and latest Debian, by default, from day one. We believe that Debian 11 and .NET 6 will be a great combination for a lot of users. Sorry <a href=\"https:\/\/www.debian.org\/releases\/buster\/\">buster<\/a>, we hit the <a href=\"https:\/\/www.debian.org\/releases\/bullseye\/\">bullseye<\/a>.<\/p>\n<p>Newer distro versions include newer major versions of various packages in their package feed and often get <a href=\"https:\/\/security-tracker.debian.org\/\">CVE fixes<\/a> faster. That&#8217;s in addition to a newer kernel. Users are better served by a new distro version.<\/p>\n<p>Looking a little further ahead, we&#8217;ll start planning support for <a href=\"https:\/\/ubuntu.com\/about\/release-cycle\">Ubuntu 22.04<\/a> before long. <a href=\"https:\/\/ubuntu.com\/\">Ubuntu<\/a> is another Debian-family distro and popular with .NET developers. We hope to offer same-day support for the new Ubuntu LTS release.<\/p>\n<p>Hat tip to <a href=\"https:\/\/github.com\/tianon\">Tianon Gravi<\/a> for maintaining Debian images for the community and helping us when we have questions.<\/p>\n<h3>Dotnet monitor<\/h3>\n<p><a href=\"https:\/\/github.com\/dotnet\/dotnet-monitor\/tree\/main\/documentation\"><code>dotnet monitor<\/code><\/a> is an important diagnostics tool for containers. It has <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-dotnet-monitor\/\">been available as a sidecar container image<\/a> for some time, but in an unsupported &#8220;experimental&#8221; status. As part of .NET 6, we are releasing a <a href=\"https:\/\/hub.docker.com\/_\/microsoft-dotnet-monitor\">.NET 6-based <code>dotnet monitor<\/code> image<\/a> that is fully-supported in production.<\/p>\n<p><a href=\"https:\/\/azure.github.io\/AppService\/2021\/11\/01\/Diagnostic-Tools-for-ASP-NET-Core-Linux-apps-are-now-publicly-available.html\"><code>dotnet monitor<\/code> is already in use by Azure App Service<\/a> as an implementation detail of their ASP.NET Core Linux diagnostics experience. This is one of the intended scenarios, building on top of dotnet monitor to provide higher-level and higher-value experiences.<\/p>\n<p>You can pull the new image now:<\/p>\n<pre><code class=\"language-bash\">docker pull mcr.microsoft.com\/dotnet\/monitor:6.0<\/code><\/pre>\n<p><code>dotnet monitor<\/code> makes it easier to access diagnostic information &#8212; logs, traces, process dumps &#8212; from a .NET process. It is easy to get access to all the diagnostic information you want on your desktop machine, however, those same familiar techniques might not work in production using containers, for example. <code>dotnet monitor<\/code> provides a unified way to collect these diagnostic artifacts regardless of whether running on your desktop machine or in a Kubernetes cluster. There are two different mechanisms for collection of these diagnostic artifacts:<\/p>\n<ul>\n<li>An <strong>HTTP API<\/strong> for ad-hoc collection of artifacts. You can call these API endpoints when you already know your application is experiencing an issue and you are interested in gathering more information.<\/li>\n<li><strong>Triggers<\/strong> for rule-based configuration for always-on collection of artifacts. You may configure rules to collect diagnostic data when a desired condition is met, for example, collect a process dump when you have sustained high CPU.<\/li>\n<\/ul>\n<p><code>dotnet monitor<\/code> provides a common diagnostic API for .NET apps that works everywhere you want with any tools you want. The &#8220;common API&#8221; isn&#8217;t a .NET API but a web API that you can call and query. <code>dotnet monitor<\/code> includes an ASP.NET web server that directly interacts with and exposes data from a diagnostics server in the .NET runtime. The design of <code>dotnet monitor<\/code> enables high-performance monitoring in production and secure use to gate access to privileged information. <code>dotnet monitor<\/code> interacts with the runtime &#8212; across container boundaries &#8212; via a non-internet-addressable <a href=\"https:\/\/en.wikipedia.org\/wiki\/Unix_domain_socket\">unix domain socket<\/a>. That model communication model is a perfect fit for this use case.<\/p>\n<h3>Structured JSON logs<\/h3>\n<p>The <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/extensions\/console-log-formatter#json\">JSON formatter<\/a> is now the default console logger in the <a href=\"https:\/\/hub.docker.com\/_\/microsoft-dotnet-aspnet\"><code>aspnet<\/code> .NET 6 container image<\/a>. The default in .NET 5 was set to the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/extensions\/console-log-formatter#simple\">simple console formatter<\/a>. This change was made in order to have a default configuration that works with automated tools that rely on a machine-readable format like JSON.<\/p>\n<p>The output now looks like the following for the <code>aspnet<\/code> image:<\/p>\n<pre><code class=\"language-bash\">$ docker run --rm -it -p 8000:80 mcr.microsoft.com\/dotnet\/samples:aspnetapp\r\n{\"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.\"}}\r\n{\"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.\"}}\r\n{\"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}\"}}\r\n{\"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.\"}}\r\n{\"EventId\":0,\"LogLevel\":\"Information\",\"Category\":\"Microsoft.Hosting.Lifetime\",\"Message\":\"Hosting environment: Production\",\"State\":{\"Message\":\"Hosting environment: Production\",\"envName\":\"Production\",\"{OriginalFormat}\":\"Hosting environment: {envName}\"}}\r\n{\"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}\"}}<\/code><\/pre>\n<p>The logger format type can be changed by setting or unsetting the <code>Logging__Console__FormatterName<\/code> environment variable or via code change (see <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/extensions\/console-log-formatter\">Console log formatting<\/a> for more details).<\/p>\n<p>After changing it, you will see output like the following (just like .NET 5):<\/p>\n<pre><code class=\"language-bash\">$ docker run --rm -it -p 8000:80 -e Logging__Console__FormatterName=\"\" mcr.microsoft.com\/dotnet\/samples:aspnetapp\r\nwarn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]\r\n      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.\r\nwarn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]\r\n      No XML encryptor configured. Key {8d4ddd1d-ccfc-4898-9fe1-3e7403bf23a0} may be persisted to storage in unencrypted form.\r\ninfo: Microsoft.Hosting.Lifetime[14]\r\n      Now listening on: http:\/\/[::]:80\r\ninfo: Microsoft.Hosting.Lifetime[0]\r\n      Application started. Press Ctrl+C to shut down.\r\ninfo: Microsoft.Hosting.Lifetime[0]\r\n      Hosting environment: Production\r\ninfo: Microsoft.Hosting.Lifetime[0]\r\n      Content root path: \/app<\/code><\/pre>\n<p>Note: This change doesn&#8217;t affect the .NET SDK on your developer machine, like with <code>dotnet run<\/code>. This change is specific to the <code>aspnet<\/code> container image.<\/p>\n<h2>Support for OpenTelemetry Metrics<\/h2>\n<p>We&#8217;ve been <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/opentelemetry-net-reaches-v1-0\/\">adding support for OpenTelemetry<\/a> for the last couple .NET versions, as part of our focus on <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/conversation-about-diagnostics\/\">observability<\/a>. In .NET 6, we&#8217;re adding <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/44445\">support<\/a> for the <a href=\"https:\/\/github.com\/open-telemetry\/opentelemetry-specification\/blob\/main\/specification\/metrics\/api.md\">OpenTelemetry Metrics API<\/a>. By adding support for OpenTelemetry, your apps can seamlessly interoperate with other <a href=\"https:\/\/opentelemetry.io\">OpenTelemetry<\/a> systems.<\/p>\n<p><a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2021\/System.Diagnostics\/Metrics-Design.md\">System.Diagnostics.Metrics<\/a> is the .NET implementation of the <a href=\"https:\/\/github.com\/open-telemetry\/opentelemetry-specification\/blob\/main\/specification\/metrics\/api.md\">OpenTelemetry Metrics API specification<\/a>. The Metrics APIs are designed explicitly for processing raw measurements, with the intent of producing continuous summaries of those measurements, efficiently and simultaneously.<\/p>\n<p>The APIs include the <code>Meter<\/code> class which can be used to create instrument objects. The APIs expose four instrument classes: <code>Counter<\/code>, <code>Histogram<\/code>, <code>ObservableCounter<\/code>, and <code>ObservableGauge<\/code> to support different metrics scenarios. Also, the APIs expose the <code>MeterListener<\/code> class to allow listening to the instrument&#8217;s recorded measurement for aggregation and grouping purposes.<\/p>\n<p>The <a href=\"https:\/\/github.com\/open-telemetry\/opentelemetry-dotnet\">OpenTelemetry .NET implementation<\/a> will be extended to use these new APIs, which add support for Metrics observability scenarios.<\/p>\n<h3>Library Measurement Recording Example<\/h3>\n<pre><code class=\"language-C#\">    Meter meter = new Meter(\"io.opentelemetry.contrib.mongodb\", \"v1.0\");\r\n    Counter&lt;int&gt; counter = meter.CreateCounter&lt;int&gt;(\"Requests\");\r\n    counter.Add(1);\r\n    counter.Add(1, KeyValuePair.Create&lt;string, object&gt;(\"request\", \"read\"));<\/code><\/pre>\n<h3>Listening Example<\/h3>\n<pre><code class=\"language-C#\">    MeterListener listener = new MeterListener();\r\n    listener.InstrumentPublished = (instrument, meterListener) =&gt;\r\n    {\r\n        if (instrument.Name == \"Requests\" &amp;&amp; instrument.Meter.Name == \"io.opentelemetry.contrib.mongodb\")\r\n        {\r\n            meterListener.EnableMeasurementEvents(instrument, null);\r\n        }\r\n    };\r\n    listener.SetMeasurementEventCallback&lt;int&gt;((instrument, measurement, tags, state) =&gt;\r\n    {\r\n        Console.WriteLine($\"Instrument: {instrument.Name} has recorded the measurement {measurement}\");\r\n    });\r\n    listener.Start();<\/code><\/pre>\n<h2>Windows Forms<\/h2>\n<p>We have continued to make key <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-windows-forms-in-net-6-0\/\">improvements in Windows Forms<\/a>. .NET 6 includes better accessibility for controls, the ability to set an application-wide default font, template updates and others.<\/p>\n<h3>Accessibility improvements<\/h3>\n<p>In this release, we added <a href=\"https:\/\/docs.microsoft.com\/windows\/win32\/winauto\/uiauto-providersoverview\">UIA providers<\/a> for <code>CheckedListBox<\/code>, <code>LinkLabel<\/code>, <code>Panel<\/code>, <code>ScrollBar<\/code>, <code>TabControl<\/code> and <code>TrackBar<\/code> that enable tools like Narrator, and test automation to interact with the elements of an application.<\/p>\n<h3>Default font<\/h3>\n<p>You can now <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-windows-forms-in-net-6-0-preview-5\/#application-wide-default-font\">set a default font for an application<\/a> with <code>Application.SetDefaultFont<\/code>.<\/p>\n<pre><code class=\"language-csharp\">void Application.SetDefaultFont(Font font)<\/code><\/pre>\n<h3>Minimal applications<\/h3>\n<p>The following is a <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2021\/winforms\/streamline-application-bootstrap.md\">minimal Windows Forms application with .NET 6<\/a>:<\/p>\n<pre><code class=\"language-csharp\">class Program\r\n{\r\n    [STAThread]\r\n    static void Main()\r\n    {\r\n        ApplicationConfiguration.Initialize();\r\n        Application.Run(new Form1());\r\n    }\r\n}<\/code><\/pre>\n<p>As part of the .NET 6 release, we&#8217;ve been updating most of the templates to them more modern and minimal, including with Windows Forms. We decided to keep the Windows Forms template a bit more traditional, in part because of the need for the <code>[STAThread]<\/code> attribute to apply to the application entrypoint. However, there is more a play than immediately meets the eye.<\/p>\n<p><code>ApplicationConfiguration.Initialize()<\/code> is a source generated API that behind the scenes emits the following calls:<\/p>\n<pre><code class=\"language-csharp\">Application.EnableVisualStyles();\r\nApplication.SetCompatibleTextRenderingDefault(false);\r\nApplication.SetDefaultFont(new Font(...));\r\nApplication.SetHighDpiMode(HighDpiMode.SystemAware);<\/code><\/pre>\n<p>The parameters of these calls are configurable via <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/project-sdk\/msbuild-props-desktop#windows-forms-settings\">MSBuild properties<\/a> in csproj or props files.<\/p>\n<p>The Windows Forms designer in Visual Studio 2022 is also aware of these properties (for now it only reads the default font), and can show you your application as it would look at runtime:<\/p>\n<p><img decoding=\"async\" class=\"alignnone\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2021\/11\/windows-forms-appbootstrap.png\" alt=\"Application bootstrap, Announcing .NET 6 \u2014 The Fastest .NET Yet\" width=\"980\" height=\"993\" \/><\/p>\n<h3>Template updates<\/h3>\n<p>Windows Forms templates for C# have been updated to support the new application bootstrap, <code>global using<\/code> directives, file-scoped namespaces, and nullable reference types.<\/p>\n<h3>More runtime designers<\/h3>\n<p>Now you can build a general-purpose designer (for example, a report designer) since .NET 6 has all the missing pieces for designers and designer-related infrastructure. For more information, see this <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-windows-forms-in-net-6-0-preview-5\/#more-runtime-designers\">blog post<\/a>.<\/p>\n<h2>Single-file Apps<\/h2>\n<p>In .NET 6, <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/43072\">in-memory single file apps have been enabled for Windows and macOS<\/a>. In .NET 5, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-5-0\/#single-file-applications\">this deployment type was limited to Linux<\/a>. You can now publish a single-file binary that is both deployed and launched as a single file, for all supported OSes. Single files apps no longer extract any <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/43072\">core runtime assemblies<\/a> to temporary directories.<\/p>\n<p>This expanded capability is based on a building block called &#8220;superhost&#8221;. &#8220;apphost&#8221; is the executable that launches your application in the non-single-file case, like <code>myapp.exe<\/code> or <code>.\/myapp<\/code>. Apphost contains code to find the runtime, load it, and start your app with that runtime. Superhost still performs some of those tasks but uses a statically linked copy of all the CoreCLR native binaries. Static linking is the approach we use to enable the single file experience.<\/p>\n<p>Native dependencies (like that come with a NuGet package) are the notable exception to single-file embedding. They are not included in the single file by default. For instance, WPF native dependencies are not part of the superhost, resulting in additional files beside the single file app. You can use the setting <code>IncludeNativeLibrariesForSelfExtract<\/code> to embed and extract native-dependencies.<\/p>\n<h3>Static Analysis<\/h3>\n<p>We&#8217;ve improved single-file analyzers to allow for custom warnings. If you have an API which doesn&#8217;t work in single-file publishing you can now mark it with the <code>[RequiresAssemblyFiles]<\/code> attribute, and a warning will appear if the analyzer is enabled. Adding that attribute will also silence all warnings related to single-file in the method, so you can use the warning to propagate warnings upward to your public API.<\/p>\n<p>The single-file analyzer is automatically enabled for <code>exe<\/code> projects when <code>PublishSingleFile<\/code> is set to <code>true<\/code>, but you can also enable it for any project by setting <code>EnableSingleFileAnalysis<\/code> to <code>true<\/code>. This is helpful if you want to support a library being part of a single file app.<\/p>\n<p>In .NET 5, we added warning for <code>Assembly.Location<\/code> and a few other APIs which behave differently in single-file bundles.<\/p>\n<h3>Compression<\/h3>\n<p>Single-file bundles now support compression, which can be enabled by setting the property <code>EnableCompressionInSingleFile<\/code> to <code>true<\/code>. At runtime, files are decompressed to memory as necessary. Compression can provide huge space savings for some scenarios.<\/p>\n<p>Let&#8217;s look at single file publishing, with and without compression, used with <a href=\"https:\/\/github.com\/NuGetPackageExplorer\/NuGetPackageExplorer\">NuGet Package Explorer<\/a>.<\/p>\n<p>Without compression: <strong>172 MB<\/strong><\/p>\n<p><img decoding=\"async\" class=\"alignnone\" src=\"https:\/\/user-images.githubusercontent.com\/515774\/115341581-b96eaa00-a15d-11eb-8ec2-a6739caa6da5.png\" alt=\"Announcing .NET 6 \u2014 The Fastest .NET Yet\" width=\"504\" height=\"299\" \/><\/p>\n<p>With compression: <strong>71.6 MB<\/strong><\/p>\n<p><img decoding=\"async\" class=\"alignnone\" src=\"https:\/\/user-images.githubusercontent.com\/515774\/115341896-39950f80-a15e-11eb-94fe-9e4c7c77118d.png\" alt=\"Announcing .NET 6 \u2014 The Fastest .NET Yet\" width=\"503\" height=\"293\" \/><\/p>\n<p>Compression can significantly increase the startup time of the application, especially on Unix platforms. Unix platforms have a no-copy fast-start path that can&#8217;t be used with compression. You should test your app after enabling compression to see if the additional startup cost is acceptable.<\/p>\n<h3>Single-file debugging<\/h3>\n<p>Single-file apps can currently only be debugged using platform debuggers, like WinDBG. We are looking at adding Visual Studio debugging with a later build of Visual Studio 2022.<\/p>\n<h3>Single-file signing on macOS<\/h3>\n<p>Single file apps now satisfy Apple notarization and signing requirements on macOS. The <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/3671\">specific changes<\/a> relate to the way that we construct single file apps in terms of discrete file layout.<\/p>\n<p>Apple started <a href=\"https:\/\/developer.apple.com\/news\/?id=09032019a\">enforcing new requirements<\/a> for <a href=\"https:\/\/developer.apple.com\/documentation\/xcode\/notarizing_macos_software_before_distribution\">signing and notarization<\/a> with <a href=\"https:\/\/support.apple.com\/kb\/SP803\">macOS Catalina<\/a>. We&#8217;ve been working closely with Apple to understand the requirements, and to find solutions that enable a development platform like .NET to work well in that environment. We&#8217;ve made <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/install\/macos-notarization-issues\">product changes and documented user workflows<\/a> to satisfy Apple requirements in each of the last few .NET releases. One of the remaining gaps was single-file signing, which is a requirement to distribute a .NET app on macOS, including in the macOS store.<\/p>\n<h2>IL trimming<\/h2>\n<p>The team has been working on IL trimming for multiple releases. .NET 6 represents a major step forward on that journey. We&#8217;ve been working to make a more aggressive trimming mode safe and predictable and as a result have confidence to make it the default. <code>TrimMode=link<\/code> was previously an opt-in feature and is now the default.<\/p>\n<p>We&#8217;ve had a three-pronged strategy to trimming:<\/p>\n<ul>\n<li>Improve trim-ability of the platform.<\/li>\n<li>Annotate the platform to provide better warnings and to enable others to do the same.<\/li>\n<li>Based on that, make the default trim mode more aggressive so that it is easy to make apps small.<\/li>\n<\/ul>\n<p>Trimming has previously been in preview because of the unreliable results for apps which use unannotated reflection. With trim warnings, the experience should now be predictable. Apps without trim warnings should trim correctly and observe no change in behavior when running. Currently, only the core .NET libraries have been fully annotated for trimming, but we hope to see the ecosystem annotate for trimming and become trim compatible<\/p>\n<h3>Reducing app size<\/h3>\n<p>Let&#8217;s take a look at this trimming improvement using <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/deploying\/ready-to-run\">crossgen<\/a>, which is one of the SDK tools. It can be trimmed with only a few trim warnings, which the crossgen team was able to resolve.<\/p>\n<p>First, let&#8217;s look at publishing crossgen as a self-contained app without trimming. It is 80 MB (which includes the .NET runtime and all the libraries).<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/515774\/115949313-18e3f700-a489-11eb-8f2d-55aad4950bea.png\" alt=\"image\" \/><\/p>\n<p>We can then try out the (now legacy) .NET 5 default trim mode, <code>copyused<\/code>. The result drops to 55 MB.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/515774\/115949175-01583e80-a488-11eb-9b2d-370e89aa1bdb.png\" alt=\"image\" \/><\/p>\n<p>The new .NET 6 default trim mode, <code>link<\/code>, drops the self-contained file size much further, to 36MB.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/515774\/115949349-652f3700-a489-11eb-83af-a5849c0c54bf.png\" alt=\"image\" \/><\/p>\n<p>We hope that the new <code>link<\/code> trim mode aligns much better with the expectations for trimming: significant savings and predictable results.<\/p>\n<h3>Warnings enabled by default<\/h3>\n<p>Trim warnings tell you about places where trimming may remove code that&#8217;s used at runtime. These warnings were previously disabled by default because the warnings were very noisy, largely due to the .NET platform not participating in trimming as a first class scenario.<\/p>\n<p>We&#8217;ve annotated large portions of the .NET libraries so that they produce accurate trim warnings. As a result, we felt it was time to enable trimming warnings by default. The ASP.NET Core and Windows Desktop runtime libraries have not been annotated. We plan to annotate ASP.NET service components next (after .NET 6). We&#8217;re hoping to see the community annotate NuGet libraries now that .NET 6 is released.<\/p>\n<p>You can disable warnings by setting <code>&lt;SuppressTrimAnalysisWarnings&gt;<\/code> to <code>true<\/code>.<\/p>\n<p>More information:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/mono\/linker\/blob\/main\/docs\/fixing-warnings.md\">Trim warnings<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/deploying\/trimming\/trim-self-contained\">Intro to trimming<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/deploying\/prepare-libraries-for-trimming\">Prepare .NET libraries for trimming<\/a><\/li>\n<\/ul>\n<h3>Shared with Native AOT<\/h3>\n<p>We&#8217;ve implemented the same trimming warnings for the <a href=\"https:\/\/github.com\/dotnet\/runtimelab\/issues\/248\">Native AOT experiment<\/a> as well, which should improve the Native AOT compilation experience in much the same way.<\/p>\n<h2>Math<\/h2>\n<p>We&#8217;ve improved Math APIs significantly. Some folks in <a href=\"https:\/\/twitter.com\/okyrylchuk\/status\/1452342483036385282\">the community have been enjoying these improvements<\/a> already.<\/p>\n<h3>Performance-oriented APIs<\/h3>\n<p>Performance-oriented math APIs have been added to System.Math. Their implementation is hardware accelerated if the underlying hardware supports it.<\/p>\n<p>New APIs:<\/p>\n<ul>\n<li><code>SinCos<\/code> for simultaneously computing <code>Sin<\/code> and <code>Cos<\/code>.<\/li>\n<li><code>ReciprocalEstimate<\/code> for computing an approximate of <code>1 \/ x<\/code>.<\/li>\n<li><code>ReciprocalSqrtEstimate<\/code> for computing an approximate of <code>1 \/ Sqrt(x)<\/code>.<\/li>\n<\/ul>\n<p>New overloads:<\/p>\n<ul>\n<li><code>Clamp<\/code>, <code>DivRem<\/code>, <code>Min<\/code>, and <code>Max<\/code> supporting <code>nint<\/code> and <code>nuint<\/code>.<\/li>\n<li><code>Abs<\/code> and <code>Sign<\/code> supporting <code>nint<\/code>.<\/li>\n<li><code>DivRem<\/code> variants which return a <code>tuple<\/code>.<\/li>\n<\/ul>\n<p>Perf improvements:<\/p>\n<ul>\n<li><code>ScaleB<\/code> was ported to C# resulting in calls being <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/42476#issuecomment-771825502\">up to 93% faster<\/a>. Credit to <a href=\"https:\/\/github.com\/alexcovington\">Alex Covington<\/a>.<\/li>\n<\/ul>\n<h3>BigInteger Performance<\/h3>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/47842\">Parsing of BigIntegers<\/a> from both decimal and hexadecimal strings has been improved. We see <a href=\"https:\/\/github.com\/DrewScoggins\/performance-2\/issues\/5765\">improvements of up to 89%<\/a>, as demonstrated in the following chart (lower is better).<\/p>\n<p><img decoding=\"async\" class=\"alignnone\" src=\"https:\/\/camo.githubusercontent.com\/4ebb3bde09bcf4e6217f1337fc16b4c8df80d1047ba2d18e7422f0dfce1244b2\/68747470733a2f2f707673636d6475706c6f61642e626c6f622e636f72652e77696e646f77732e6e65742f6175746f66696c657265706f72742f6175746f66696c657265706f7274732f30355f31315f323032312f726566732f68656164732f6d61696e5f7836345f57696e646f777325323031302e302e31383336325f496d70726f76656d656e742f53797374656d2e4e756d65726963732e54657374732e506572665f426967496e74656765725f342e706e67\" alt=\"graph, Announcing .NET 6 \u2014 The Fastest .NET Yet\" width=\"1000\" height=\"350\" \/><\/p>\n<p>Credit to <a href=\"https:\/\/github.com\/jfd16\">Joseph Da Silva<\/a>.<\/p>\n<h3><code>Complex<\/code> APIs now annotated as <code>readonly<\/code><\/h3>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/51797\/\">Various <code>System.Numerics.Complex<\/code> APIs are now annotated as <code>readonly<\/code><\/a> to ensure that no copy is made for <code>readonly<\/code> values or values passed by <code>in<\/code>.<\/p>\n<p>Credit to <a href=\"https:\/\/github.com\/hrrrrustic\">hrrrrustic<\/a>.<\/p>\n<h3><code>BitConverter<\/code> now supports floating-point to unsigned integer bitcasts<\/h3>\n<p><code>BitConverter<\/code> <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/53784\">now supports <code>DoubleToUInt64Bits<\/code>, <code>HalfToUInt16Bits<\/code>, <code>SingleToUInt32Bits<\/code>, <code>UInt16BitsToHalf<\/code>, <code>UInt32BitsToSingle<\/code>, and <code>UInt64BitsToDouble<\/code><\/a>. This should make it easier to do floating-point bit manipulation when required.<\/p>\n<p>Credit to <a href=\"https:\/\/github.com\/MichalPetryka\">Michal Petryka<\/a>.<\/p>\n<h3><code>BitOperations<\/code> supports additional functionality<\/h3>\n<p><code>BitOperations<\/code> now supports <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/36163\"><code>IsPow2<\/code><\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/53992\"><code>RoundUpToPowerOf2<\/code><\/a>, and <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/58733\">provides <code>nint<\/code>\/<code>nuint<\/code> overloads for existing functions<\/a>.<\/p>\n<p>Credit to <a href=\"https:\/\/github.com\/john-h-k\">John Kelly<\/a>, <a href=\"https:\/\/github.com\/huoyaoyuan\">Huo Yaoyuan<\/a>, and <a href=\"https:\/\/github.com\/deeprobin\">Robin Lindner<\/a>.<\/p>\n<h3><code>Vector&lt;T&gt;<\/code>, <code>Vector2<\/code>, <code>Vector3<\/code>, and <code>Vector4<\/code> improvements<\/h3>\n<p><code>Vector&lt;T&gt;<\/code> <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/50832\">now supports the <code>nint<\/code> and <code>nuint<\/code> primitive types<\/a>, added in C# 9. For example, this change should make it simpler to use SIMD instructions with pointers or platform-dependent length types.<\/p>\n<p><code>Vector&lt;T&gt;<\/code> <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/53527\">now supports a <code>Sum<\/code> method<\/a> to simplify needing to compute the &#8220;horizontal sum&#8221; of all elements in the vector. Credit to <a href=\"https:\/\/github.com\/zlatanov\">Ivan Zlatanov<\/a>.<\/p>\n<p><code>Vector&lt;T&gt;<\/code> <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/47150\">now supports a generic <code>As&lt;TFrom, TTo&gt;<\/code> method<\/a> to simplify dealing with vectors in generic contexts where the concrete type isn&#8217;t known. Credit to <a href=\"https:\/\/github.com\/huoyaoyuan\">Huo Yaoyuan<\/a><\/p>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/50062\">Overloads supporting <code>Span&lt;T&gt;<\/code><\/a> were added to <code>Vector2<\/code>, <code>Vector3<\/code>, and <code>Vector4<\/code> to improve the experience when needing to load or store vector types.<\/p>\n<h3>Better parsing of standard numeric formats<\/h3>\n<p>We&#8217;ve <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/46827\">improved the parser for the standard numeric types<\/a>, specifically for <code>.ToString<\/code> and <code>.TryFormat<\/code>. They will now understand requests for precision &gt; 99 decimal places and will provide accurate results to that many digits. Also, the parser now better supports trailing zeros in the <code>Parse<\/code> method.<\/p>\n<p>The following examples demonstrate before and after behavior.<\/p>\n<ul>\n<li><code>32.ToString(\"C100\")<\/code> -&gt; <code>C132<\/code>\n<ul>\n<li>.NET 6: <code>$32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000<\/code><\/li>\n<li>.NET 5: We had an artificial limitation in the formatting code to only handle a precision of &lt;= 99. For precision &gt;= 100, we instead interpreted the input as a custom format.<\/li>\n<\/ul>\n<\/li>\n<li><code>32.ToString(\"H99\")<\/code> -&gt; throw a <code>FormatException<\/code>\n<ul>\n<li>.NET 6: throws a FormatException<\/li>\n<li>This is correct behavior, but it&#8217;s called here to contrast with the next example.<\/li>\n<\/ul>\n<\/li>\n<li><code>32.ToString(\"H100\")<\/code> -&gt; <code>H132<\/code>\n<ul>\n<li>.NET 6: throw a FormatException<\/li>\n<li>.NET 5: <code>H<\/code> is an invalid format specifier. So, we should&#8217;ve thrown a <code>FormatException<\/code>. Instead, our incorrect behavior of interpreting precision &gt;= 100 as custom formats meant we returned wrong values.<\/li>\n<\/ul>\n<\/li>\n<li><code>double.Parse(\"9007199254740997.0\")<\/code> -&gt; <code>9007199254740998<\/code>\n<ul>\n<li>.NET 6: <code>9007199254740996<\/code>.<\/li>\n<li>.NET 5: <code>9007199254740997.0<\/code> is not exactly representable in the IEEE 754 format. With our current rounding scheme, the correct return value should have been <code>9007199254740996<\/code>. However, the last <code>.0<\/code> portion of the input was forcing the parser to incorrectly round the result and return <code>9007199254740998<\/code>.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>System.Text.Json<\/h2>\n<p><code>System.Text.Json<\/code> provides multiple high-performance APIs for processing JSON documents. Over the last few releases, we&#8217;ve added new functionality to further improve JSON processing performance and to mitigate blockers for people wanting to migrate from <a href=\"https:\/\/www.nuget.org\/packages\/Newtonsoft.Json\"><code>NewtonSoft.Json<\/code><\/a>. This release includes continues on that path and is major step forward on performance, particularly with the serializer source generator.<\/p>\n<h3><code>JsonSerializer<\/code> source generation<\/h3>\n<p>Note: Apps that <a href=\"https:\/\/github.com\/dotnet\/core\/issues\/6570#issuecomment-938637761\">used source generation with .NET 6 RC1 or earlier builds should be re-compiled<\/a>.<\/p>\n<p>The backbone of nearly all .NET serializers is reflection. Reflection is a great capability for certain scenarios, but not as the basis of high-performance cloud-native applications (which typically (de)serialize and process a lot of JSON documents). Reflection is a problem for <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/1568\">startup<\/a>, memory usage, and <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/45441\">assembly trimming<\/a>.<\/p>\n<p>The alternative to runtime reflection is compile-time <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/new-c-source-generator-samples\/\">source generation<\/a>. In .NET 6, we are including a <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/try-the-new-system-text-json-source-generator\/\">new source generator as part of <code>System.Text.Json<\/code><\/a>. The JSON source generator works in conjunction with <code>JsonSerializer<\/code> and can be configured in multiple ways.<\/p>\n<p>It can provide the following benefits:<\/p>\n<ul>\n<li>Reduce start-up time<\/li>\n<li>Improve serialization throughput<\/li>\n<li>Reduce private memory usage<\/li>\n<li>Remove runtime use of <code>System.Reflection<\/code> and <code>System.Reflection.Emit<\/code><\/li>\n<li>IL trimming compatibility<\/li>\n<\/ul>\n<p>By default, the JSON source generator emits serialization logic for the given serializable types. This delivers higher performance than using the existing <code>JsonSerializer<\/code> methods by generating source code that uses <code>Utf8JsonWriter<\/code> directly. In short, source generators offer a way of giving you a different implementation at compile-time in order to make the runtime experience better.<\/p>\n<p>Given a simple type:<\/p>\n<pre><code class=\"language-cs\">namespace Test\r\n{\r\n    internal class JsonMessage\r\n    {\r\n        public string Message { get; set; }\r\n    }\r\n}<\/code><\/pre>\n<p>The source generator can be configured to generate serialization logic for instances of the example <code>JsonMessage<\/code> type. Note that the class name <code>JsonContext<\/code> is arbitrary. You can use whichever class name you want for the generated source.<\/p>\n<pre><code class=\"language-cs\">using System.Text.Json.Serialization;\r\n\r\nnamespace Test\r\n{\r\n    [JsonSerializable(typeof(JsonMessage)]\r\n    internal partial class JsonContext : JsonSerializerContext\r\n    {\r\n    }\r\n}<\/code><\/pre>\n<p>The serializer invocation with this mode could look like the following example. This example provides the best possible performance.<\/p>\n<pre><code class=\"language-cs\">using MemoryStream ms = new();\r\nusing Utf8JsonWriter writer = new(ms);\r\n\r\nJsonSerializer.Serialize(jsonMessage, JsonContext.Default.JsonMessage);\r\nwriter.Flush();\r\n\r\n\/\/ Writer contains:\r\n\/\/ {\"Message\":\"Hello, world!\"}<\/code><\/pre>\n<p>The fastest and most optimized source generation mode &#8212; based on <code>Utf8JsonWriter<\/code> &#8212; is currently only available for serialization. Similar support for deserialization &#8212; based on <code>Utf8JsonReader<\/code> &#8212; may be provided in the future depending on your feedback.<\/p>\n<p>The source generator also emits type-metadata initialization logic that can benefit deserialization as well. To deserialize an instance of <code>JsonMessage<\/code> using pre-generated type metadata, you can do the following:<\/p>\n<pre><code class=\"language-cs\">JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);<\/code><\/pre>\n<h3><code>JsonSerializer<\/code> support for IAsyncEnumerable<\/h3>\n<p>You can now <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/1570\">(de)serialize <code>IAsyncEnumerable&lt;T&gt;<\/code> JSON arrays<\/a> with <code>System.Text.Json<\/code>.The following examples use streams as a representation of any async source of data. The source could be files on a local machine, or results from a database query or web service API call.<\/p>\n<p><code>JsonSerializer.SerializeAsync<\/code> has been updated to recognize and provide special handing for <code>IAsyncEnumerable<\/code> values.<\/p>\n<pre><code class=\"language-csharp\">using System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Text.Json;\r\n\r\nstatic async IAsyncEnumerable&lt;int&gt; PrintNumbers(int n)\r\n{\r\n    for (int i = 0; i &lt; n; i++) yield return i;\r\n}\r\n\r\nusing Stream stream = Console.OpenStandardOutput();\r\nvar data = new { Data = PrintNumbers(3) };\r\nawait JsonSerializer.SerializeAsync(stream, data); \/\/ prints {\"Data\":[0,1,2]}<\/code><\/pre>\n<p><code>IAsyncEnumerable<\/code> values are only supported using the asynchronous serialization methods. Attempting to serialize using the synchronous methods will result in a <code>NotSupportedException<\/code> being thrown.<\/p>\n<p>Streaming deserialization required a new API that returns <code>IAsyncEnumerable&lt;T&gt;<\/code>. We added the <code>JsonSerializer.DeserializeAsyncEnumerable<\/code> method for this purpose, as you can see in the following example.<\/p>\n<pre><code class=\"language-csharp\">using System;\r\nusing System.IO;\r\nusing System.Text;\r\nusing System.Text.Json;\r\n\r\nvar stream = new MemoryStream(Encoding.UTF8.GetBytes(\"[0,1,2,3,4]\"));\r\nawait foreach (int item in JsonSerializer.DeserializeAsyncEnumerable&lt;int&gt;(stream))\r\n{\r\n    Console.WriteLine(item);\r\n}<\/code><\/pre>\n<p>This example will deserialize elements on-demand and can be useful when consuming particularly large data streams. It only supports reading from root-level JSON arrays, although that could potentially be relaxed in the future based on feedback.<\/p>\n<p>The existing <code>DeserializeAsync<\/code> method nominally supports <code>IAsyncEnumerable&lt;T&gt;<\/code>, but within the confines of its non-streaming method signature. It must return the final result as a single value, as you can see in the following example.<\/p>\n<pre><code class=\"language-csharp\">using System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Text;\r\nusing System.Text.Json;\r\n\r\nvar stream = new MemoryStream(Encoding.UTF8.GetBytes(@\"{\"\"Data\"\":[0,1,2,3,4]}\"));\r\nvar result = await JsonSerializer.DeserializeAsync&lt;MyPoco&gt;(stream);\r\nawait foreach (int item in result.Data)\r\n{\r\n    Console.WriteLine(item);\r\n}\r\n\r\npublic class MyPoco\r\n{\r\n    public IAsyncEnumerable&lt;int&gt; Data { get; set; }\r\n}<\/code><\/pre>\n<p>In this example, the deserializer will have buffered all <code>IAsyncEnumerable<\/code> contents in memory before returning the deserialized object. This is because the deserializer needs to have consumed the entire JSON value before returning a result.<\/p>\n<h3>System.Text.Json: Writable DOM Feature<\/h3>\n<p>The <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2020\/serializer\/WriteableDomAndDynamic.md\">writeable JSON DOM feature<\/a> adds a <a href=\"https:\/\/github.com\/dotnet\/core\/issues\/6098#issuecomment-840857013\">new straightforward and high-performance programming model<\/a> for <code>System.Text.Json<\/code>. This new API is attractive since it avoids needing strongly-typed serialization contracts, and the DOM is mutable as opposed to the existing <code>JsonDocument<\/code> type.<\/p>\n<p>This new API has the following benefits:<\/p>\n<ul>\n<li>A lightweight alternative to serialization for cases when using <a href=\"https:\/\/en.wikipedia.org\/wiki\/Plain_old_CLR_object\">POCO<\/a> types is not possible or desired, or when a JSON schema is not fixed and must be inspected.<\/li>\n<li>Enables efficient modification of a subset of a large tree. For example, it is possible to efficiently navigate to a subsection of a large JSON tree and read an array or deserialize a POCO from that subsection. LINQ can also be used with that.<\/li>\n<\/ul>\n<p>The following example demonstrates the new programming model.<\/p>\n<pre><code class=\"language-cs\">    \/\/ Parse a JSON object\r\n    JsonNode jNode = JsonNode.Parse(\"{\"MyProperty\":42}\");\r\n    int value = (int)jNode[\"MyProperty\"];\r\n    Debug.Assert(value == 42);\r\n    \/\/ or\r\n    value = jNode[\"MyProperty\"].GetValue&lt;int&gt;();\r\n    Debug.Assert(value == 42);\r\n\r\n    \/\/ Parse a JSON array\r\n    jNode = JsonNode.Parse(\"[10,11,12]\");\r\n    value = (int)jNode[1];\r\n    Debug.Assert(value == 11);\r\n    \/\/ or\r\n    value = jNode[1].GetValue&lt;int&gt;();\r\n    Debug.Assert(value == 11);\r\n\r\n    \/\/ Create a new JsonObject using object initializers and array params\r\n    var jObject = new JsonObject\r\n    {\r\n        [\"MyChildObject\"] = new JsonObject\r\n        {\r\n            [\"MyProperty\"] = \"Hello\",\r\n            [\"MyArray\"] = new JsonArray(10, 11, 12)\r\n        }\r\n    };\r\n\r\n    \/\/ Obtain the JSON from the new JsonObject\r\n    string json = jObject.ToJsonString();\r\n    Console.WriteLine(json); \/\/ {\"MyChildObject\":{\"MyProperty\":\"Hello\",\"MyArray\":[10,11,12]}}\r\n\r\n    \/\/ Indexers for property names and array elements are supported and can be chained\r\n    Debug.Assert(jObject[\"MyChildObject\"][\"MyArray\"][1].GetValue&lt;int&gt;() == 11);<\/code><\/pre>\n<h3>ReferenceHandler.IgnoreCycles<\/h3>\n<p><a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.jsonserializer\"><code>JsonSerializer<\/code> (System.Text.Json)<\/a> now supports the ability to <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/40099\">ignore cycles when serializing an object graph<\/a>. The <code>ReferenceHandler.IgnoreCycles<\/code> option has similar behavior as Newtonsoft.Json <a href=\"https:\/\/www.newtonsoft.com\/json\/help\/html\/ReferenceLoopHandlingIgnore.htm\"><code>ReferenceLoopHandling.Ignore<\/code><\/a>. One key difference is that the System.Text.Json implementation replaces reference loops with the <code>null<\/code> JSON token instead of ignoring the object reference.<\/p>\n<p>You can see the behavior of <code>ReferenceHandler.IgnoreCycles<\/code> in the following example. In this case, the <code>Next<\/code> property is serialized as <code>null<\/code> since it otherwise creates a cycle.<\/p>\n<pre><code class=\"language-csharp\">class Node\r\n{\r\n    public string Description { get; set; }\r\n    public object Next { get; set; }\r\n}\r\n\r\nvoid Test()\r\n{\r\n    var node = new Node { Description = \"Node 1\" };\r\n    node.Next = node;\r\n\r\n    var opts = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles };\r\n\r\n    string json = JsonSerializer.Serialize(node, opts);\r\n    Console.WriteLine(json); \/\/ Prints {\"Description\":\"Node 1\",\"Next\":null}\r\n}<\/code><\/pre>\n<h2>Source build<\/h2>\n<p>With source build, <a href=\"https:\/\/github.com\/dotnet\/source-build#building\">you can build the .NET SDK on your own machine from source<\/a> with just a few commands. Let me explain why this project is important.<\/p>\n<p><a href=\"https:\/\/github.com\/dotnet\/source-build\">Source build<\/a> is a scenario and also infrastructure that we\u2019ve been working on in collaboration with Red Hat since before shipping .NET Core 1.0. Several years later, we\u2019re very close to delivering a fully automated version of it. For Red Hat Enterprise Linux (RHEL) .NET users, this <a href=\"https:\/\/www.redhat.com\/en\/about\/development-model\">capability is a big deal<\/a>. Red Hat tells us that .NET has grown to become an important developer platform for their ecosystem. Nice!<\/p>\n<p>The <a href=\"https:\/\/wiki.debian.org\/Packaging\/SourcePackage\">gold standard for Linux distros<\/a> is to build open source code using compilers and toolchains that are part of the distro archive. That works for the .NET runtime (written in C++), but not for any of the code written in C#. For C# code, we use a two-pass build mechanism to satisfy distro requirements. It\u2019s a bit complicated, but it\u2019s important to understand the flow.<\/p>\n<p>Red Hat builds .NET SDK source using the Microsoft binary build of the .NET SDK (#1) to produce a pure open source binary build of the SDK (#2). After that, the same SDK source code is built again using this fresh build of the SDK (#2) to produce a provably open source SDK (#3). This final binary build of the .NET SDK (#3) is then made available to RHEL users. After that, Red Hat can use this same SDK (#3) to build new .NET versions and no longer needs to use the Microsoft SDK to build monthly updates.<\/p>\n<p>That process may be surprising and confusing. Open source distros need to be built by open source tools. This pattern ensures that the Microsoft build of the SDK isn\u2019t required, either by intention or accident. There is a higher bar, as a developer platform, to being included in a distro than just using a compatible license. The source build project has enabled .NET to meet that bar.<\/p>\n<p>The deliverable for source build is a source tarball. The source tarball contains all the source for the SDK (for a given release). From there, Red Hat (or another organization) can build their own version of the SDK. Red Hat policy requires using a built-from-source toolchain to produce a binary tar ball, which is why they use a two-pass methodology. But this two-pass method is not required for source build itself.<\/p>\n<p>It is common in the Linux ecosystem to have both source and binary packages or tarballs available for a given component. We already had binary tarballs available and now have source tarballs as well. That makes .NET match the standard component pattern.<\/p>\n<p>The big improvement in .NET 6 is that the source tarball is a now a product of our build. It used to require significant manual effort to produce, which also resulted in significant latency delivering the source tarball to Red Hat. Neither party was happy about that.<\/p>\n<p>We\u2019ve been working closely with Red Hat on this project for five+ years. It has succeeded, in no small part, due to the efforts of the excellent Red Hat engineers we\u2019ve had the pleasure of working with. Other distros and organizations have and will benefit from their efforts.<\/p>\n<p>As a side note, source build is a big step towards <a href=\"https:\/\/wiki.debian.org\/ReproducibleBuilds\">reproducible builds<\/a>, which we also strongly believe in. The .NET SDK and C# compiler have significant reproducible build capabilities.<\/p>\n<h2>Libraries APIs<\/h2>\n<p>The following APIs have been added, in the addition to the ones already covered.<\/p>\n<h3>WebSocket Compression<\/h3>\n<p>Compression is important for any data transmitted over a network. <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/20004\">WebSockets now enable compression<\/a>. We used an implementation of <code>permessage-deflate<\/code> extension for WebSockets, <a href=\"https:\/\/tools.ietf.org\/html\/rfc7692\">RFC 7692<\/a>. It allows compressing WebSockets message payloads using the <code>DEFLATE<\/code> algorithm. This <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/31088\">feature<\/a> was one of the top user requests for Networking on GitHub.<\/p>\n<p>Compression used with encryption may lead to attacks, like <a href=\"https:\/\/en.wikipedia.org\/wiki\/CRIME\">CRIME<\/a> and <a href=\"https:\/\/en.wikipedia.org\/wiki\/BREACH\">BREACH<\/a>. It means that a secret cannot be sent together with user-generated data in a single compression context, otherwise that secret could be extracted. To bring a user&#8217;s attention to these implications and help them weigh the risks, we named one of the key APIs <code>DangerousDeflateOptions<\/code>. We also added the ability to turn off compression for specific messages, so if the user would want to send a secret, they could do that securely without compression.<\/p>\n<p>The <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/52022\">memory footprint of the WebSocket when compression is disabled<\/a> was reduced by about 27%.<\/p>\n<p>Enabling the compression from the client side is easy, as you can see in the following example. However, bear in mind that the server can negotiate the settings, such as, requesting a smaller window or denying compression completely.<\/p>\n<pre><code class=\"language-c#\">var cws = new ClientWebSocket();\r\ncws.Options.DangerousDeflateOptions = new WebSocketDeflateOptions()\r\n{\r\n    ClientMaxWindowBits = 10,\r\n    ServerMaxWindowBits = 10\r\n};<\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\/2715\">WebSocket compression support for ASP.NET Core<\/a> was also added.<\/p>\n<p>Credit to <a href=\"https:\/\/github.com\/zlatanov\">Ivan Zlatanov<\/a>.<\/p>\n<h3>Socks proxy support<\/h3>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/SOCKS\">SOCKS<\/a> is a proxy server implementation that can process any TCP or UDP traffic, making it a very versatile system. It is a <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/17740\">long-standing community request<\/a> that has been <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/48883\">added to .NET 6<\/a>.<\/p>\n<p>This change adds support for Socks4, Socks4a, and Socks5. For example, it enables testing external connections via SSH or <a href=\"https:\/\/en.wikipedia.org\/wiki\/SOCKS#Usage\">connecting to the Tor network<\/a>.<\/p>\n<p>The <code>WebProxy<\/code> class now accepts <code>socks<\/code> schemes, as you can see in the following example.<\/p>\n<pre><code class=\"language-c#\">var handler = new HttpClientHandler\r\n{\r\n    Proxy = new WebProxy(\"socks5:\/\/127.0.0.1\", 9050)\r\n};\r\nvar httpClient = new HttpClient(handler);<\/code><\/pre>\n<p>Credit to <a href=\"https:\/\/github.com\/huoyaoyuan\">Huo Yaoyuan<\/a>.<\/p>\n<h3>Microsoft.Extensions.Hosting &#8212; ConfigureHostOptions API<\/h3>\n<p>We added a new ConfigureHostOptions API on IHostBuilder to make application setup simpler (for example, configuring the shutdown timeout):<\/p>\n<pre><code class=\"language-c#\">using HostBuilder host = new()\r\n    .ConfigureHostOptions(o =&gt;\r\n    {\r\n        o.ShutdownTimeout = TimeSpan.FromMinutes(10);\r\n    })\r\n    .Build();\r\n\r\nhost.Run();<\/code><\/pre>\n<p>In .NET 5, configuring the host options was a bit more complicated:<\/p>\n<pre><code class=\"language-c#\">using HostBuilder host = new()\r\n    .ConfigureServices(services =&gt;\r\n    {\r\n        services.Configure&lt;HostOptions&gt;(o =&gt;\r\n        {\r\n            o.ShutdownTimeout = TimeSpan.FromMinutes(10);\r\n        });\r\n    })\r\n    .Build();\r\n\r\nhost.Run();<\/code><\/pre>\n<h3>Microsoft.Extensions.DependencyInjection &#8212; CreateAsyncScope APIs<\/h3>\n<p>The <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/51840\"><code>CreateAsyncScope<\/code> API<\/a> was created to handle disposal of <code>IAsyncDisposable<\/code> services. Previously, you might have noticed that disposal of a <code>IAsyncDisposable<\/code> service provider could throw an <code>InvalidOperationException<\/code> exception.<\/p>\n<p>The following example demonstrates the new pattern, using <code>CreateAsyncScope<\/code> to enable safe use of the <code>using<\/code> statement.<\/p>\n<pre><code class=\"language-c#\">await using (var scope = provider.CreateAsyncScope())\r\n{\r\n    var foo = scope.ServiceProvider.GetRequiredService&lt;Foo&gt;();\r\n}<\/code><\/pre>\n<p>The following example demonstrate the existing problem case:<\/p>\n<pre><code class=\"language-c#\">using System;\r\nusing System.Threading.Tasks;\r\nusing Microsoft.Extensions.DependencyInjection;\r\n\r\nawait using var provider = new ServiceCollection()\r\n        .AddScoped&lt;Foo&gt;()\r\n        .BuildServiceProvider();\r\n\r\n\/\/ This using can throw InvalidOperationException\r\nusing (var scope = provider.CreateScope())\r\n{\r\n    var foo = scope.ServiceProvider.GetRequiredService&lt;Foo&gt;();\r\n}\r\n\r\nclass Foo : IAsyncDisposable\r\n{\r\n    public ValueTask DisposeAsync() =&gt; default;\r\n}<\/code><\/pre>\n<p>The following pattern was the previous suggested workaround to avoid the exception. It is no longer required.<\/p>\n<pre><code class=\"language-c#\">var scope = provider.CreateScope();\r\nvar foo = scope.ServiceProvider.GetRequiredService&lt;Foo&gt;();\r\nawait ((IAsyncDisposable)scope).DisposeAsync();<\/code><\/pre>\n<p>Credit to <a href=\"https:\/\/github.com\/bjorkstromm\">Martin Bj\u00f6rkstr\u00f6m<\/a>.<\/p>\n<h3>Microsoft.Extensions.Logging &#8212; compile-time source generator<\/h3>\n<p>.NET 6 <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/52549\">introduces the <code>LoggerMessageAttribute<\/code> type<\/a>. This attribute is part of the <code>Microsoft.Extensions.Logging<\/code> namespace, and when used, it source-generates performant logging APIs. The source-generation logging support is designed to deliver a highly usable and highly performant logging solution for modern .NET applications. The auto-generated source code relies on the <code>ILogger<\/code> interface in conjunction with <code>LoggerMessage.Define<\/code> functionality.<\/p>\n<p>The source generator is triggered when the <code>LoggerMessageAttribute<\/code> is used on <code>partial<\/code> logging methods. When triggered, it is either able to autogenerate the implementation of the <code>partial<\/code> methods it&#8217;s decorating or produce compile-time diagnostics with hints about proper usage. The compile-time logging solution is typically considerably faster at runtime than existing logging approaches. It achieves this by eliminating boxing, temporary allocations, and copies to the maximum extent possible.<\/p>\n<p>There are benefits over manually using <code>LoggerMessage.Define<\/code> APIs directly:<\/p>\n<ul>\n<li>Shorter and simpler syntax: Declarative attribute usage rather than coding boilerplate.<\/li>\n<li>Guided developer experience: The generator gives warnings to help developers do the right thing.<\/li>\n<li>Support for an arbitrary number of logging parameters. <code>LoggerMessage.Define<\/code> supports a maximum of six.<\/li>\n<li>Support for dynamic log level. This is not possible with <code>LoggerMessage.Define<\/code> alone.<\/li>\n<\/ul>\n<p>To use the <code>LoggerMessageAttribute<\/code>, the consuming class and method need to be <code>partial<\/code>. The code generator is triggered at compile time and generates an implementation of the <code>partial<\/code> method.<\/p>\n<pre><code class=\"language-csharp\">public static partial class Log\r\n{\r\n    [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = \"Could not open socket to `{hostName}`\")]\r\n    public static partial void CouldNotOpenSocket(ILogger logger, string hostName);\r\n}<\/code><\/pre>\n<p>In the preceding example, the logging method is <code>static<\/code> and the log level is specified in the attribute definition. When using the attribute in a static context, the <code>ILogger<\/code> instance is required as a parameter. You may choose to use the attribute in a non-static context as well. For more examples and usage scenarios, visit the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/extensions\/logger-message-generator\">compile-time logging source generator<\/a> documentation.<\/p>\n<h3>System.Linq &#8212; Enumerable support for <code>Index<\/code> and <code>Range<\/code> parameters<\/h3>\n<p>The <code>Enumerable.ElementAt<\/code> method now accepts indices from the end of the enumerable, as you can see in the following example.<\/p>\n<pre><code class=\"language-csharp\">Enumerable.Range(1, 10).ElementAt(^2); \/\/ returns 9<\/code><\/pre>\n<p>An <code>Enumerable.Take<\/code> overload has been added that accepts <code>Range<\/code> parameters. It simplifies taking slices of enumerable sequences:<\/p>\n<ul>\n<li><code>source.Take(..3)<\/code> instead of <code>source.Take(3)<\/code><\/li>\n<li><code>source.Take(3..)<\/code> instead of <code>source.Skip(3)<\/code><\/li>\n<li><code>source.Take(2..7)<\/code> instead of <code>source.Take(7).Skip(2)<\/code><\/li>\n<li><code>source.Take(^3..)<\/code> instead of <code>source.TakeLast(3)<\/code><\/li>\n<li><code>source.Take(..^3)<\/code> instead of <code>source.SkipLast(3)<\/code><\/li>\n<li><code>source.Take(^7..^3)<\/code> instead of <code>source.TakeLast(7).SkipLast(3)<\/code>.<\/li>\n<\/ul>\n<p>Credit to <a href=\"https:\/\/github.com\/dixin\">@dixin<\/a>.<\/p>\n<h3>System.Linq &#8212; <code>TryGetNonEnumeratedCount<\/code><\/h3>\n<p>The <code>TryGetNonEnumeratedCount<\/code> method attempts to obtain the count of the source enumerable without forcing an enumeration. This approach can be useful in scenarios where it is useful to preallocate buffers ahead of enumeration, as you can see in the following example.<\/p>\n<pre><code class=\"language-csharp\">List&lt;T&gt; buffer = source.TryGetNonEnumeratedCount(out int count) ? new List&lt;T&gt;(capacity: count) : new List&lt;T&gt;();\r\nforeach (T item in source)\r\n{\r\n    buffer.Add(item);\r\n}<\/code><\/pre>\n<p><code>TryGetNonEnumeratedCount<\/code> checks for sources implementing <code>ICollection<\/code>\/<code>ICollection&lt;T&gt;<\/code> or takes advantage of some of the <a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/a123d28793ad22954ca6d9074f43b540d0d30b43\/src\/libraries\/System.Linq\/src\/System\/Linq\/IIListProvider.cs\">internal optimizations employed by Linq<\/a>.<\/p>\n<h3>System.Linq &#8212; <code>DistinctBy<\/code>\/<code>UnionBy<\/code>\/<code>IntersectBy<\/code>\/<code>ExceptBy<\/code><\/h3>\n<p>New variants have been added to the set operations that allow specifying equality using key selector functions, as you can see in the following example.<\/p>\n<pre><code class=\"language-csharp\">Enumerable.Range(1, 20).DistinctBy(x =&gt; x % 3); \/\/ {1, 2, 3}\r\n\r\nvar first = new (string Name, int Age)[] { (\"Francis\", 20), (\"Lindsey\", 30), (\"Ashley\", 40) };\r\nvar second = new (string Name, int Age)[] { (\"Claire\", 30), (\"Pat\", 30), (\"Drew\", 33) };\r\nfirst.UnionBy(second, person =&gt; person.Age); \/\/ { (\"Francis\", 20), (\"Lindsey\", 30), (\"Ashley\", 40), (\"Drew\", 33) }<\/code><\/pre>\n<h3>System.Linq &#8212; <code>MaxBy<\/code>\/<code>MinBy<\/code><\/h3>\n<p><code>MaxBy<\/code> and <code>MinBy<\/code> methods allow finding maximal or minimal elements using a key selector, as you can see in the following example.<\/p>\n<pre><code class=\"language-csharp\">var people = new (string Name, int Age)[] { (\"Francis\", 20), (\"Lindsey\", 30), (\"Ashley\", 40) };\r\npeople.MaxBy(person =&gt; person.Age); \/\/ (\"Ashley\", 40)<\/code><\/pre>\n<h3>System.Linq &#8212; <code>Chunk<\/code><\/h3>\n<p><code>Chunk<\/code> can be used to chunk a source enumerable into slices of a fixed size, as you can see in the following example.<\/p>\n<pre><code class=\"language-csharp\">IEnumerable&lt;int[]&gt; chunks = Enumerable.Range(0, 10).Chunk(size: 3); \/\/ { {0,1,2}, {3,4,5}, {6,7,8}, {9} }<\/code><\/pre>\n<p>Credit to <a href=\"https:\/\/github.com\/inputfalken\">Robert Andersson<\/a>.<\/p>\n<h3>System.Linq &#8212; <code>FirstOrDefault<\/code>\/<code>LastOrDefault<\/code>\/<code>SingleOrDefault<\/code> overloads taking default parameters<\/h3>\n<p>The existing <code>FirstOrDefault<\/code>\/<code>LastOrDefault<\/code>\/<code>SingleOrDefault<\/code> methods return <code>default(T)<\/code> if the source enumerable is empty. New overloads have been added that accept a default parameter to be returned in that case, as you can see in the following example.<\/p>\n<pre><code class=\"language-csharp\">Enumerable.Empty&lt;int&gt;().SingleOrDefault(-1); \/\/ returns -1<\/code><\/pre>\n<p>Credit to <a href=\"https:\/\/github.com\/Foxtrek64\">@Foxtrek64<\/a>.<\/p>\n<h3>System.Linq &#8212; <code>Zip<\/code> overload accepting three enumerables<\/h3>\n<p>The <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.linq.enumerable.zip\">Zip<\/a> method now supports combining three enumerables, as you can see in the following example.<\/p>\n<pre><code class=\"language-csharp\">var xs = Enumerable.Range(1, 10);\r\nvar ys = xs.Select(x =&gt; x.ToString());\r\nvar zs = xs.Select(x =&gt; x % 2 == 0);\r\n\r\nforeach ((int x, string y, bool z) in Enumerable.Zip(xs,ys,zs))\r\n{\r\n}<\/code><\/pre>\n<p>Credit to <a href=\"https:\/\/github.com\/huoyaoyuan\">Huo Yaoyuan<\/a>.<\/p>\n<h3>PriorityQueue<\/h3>\n<p><code>PriorityQueue&lt;TElement, TPriority&gt;<\/code> (System.Collections.Generic) is a <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/46009\">new collection<\/a> that enables adding new items with a value and a priority. On dequeue the PriorityQueue returns the element with the lowest priority value. You can think of this new collection as similar to <code>Queue&lt;T&gt;<\/code> but that each enqueued element has a priority value that affects the behavior of dequeue.<\/p>\n<p>The following sample demonstrates the behavior of <code>PriorityQueue&lt;string, int&gt;<\/code>.<\/p>\n<pre><code class=\"language-csharp\">\/\/ creates a priority queue of strings with integer priorities\r\nvar pq = new PriorityQueue&lt;string, int&gt;();\r\n\r\n\/\/ enqueue elements with associated priorities\r\npq.Enqueue(\"A\", 3);\r\npq.Enqueue(\"B\", 1);\r\npq.Enqueue(\"C\", 2);\r\npq.Enqueue(\"D\", 3);\r\n\r\npq.Dequeue(); \/\/ returns \"B\"\r\npq.Dequeue(); \/\/ returns \"C\"\r\npq.Dequeue(); \/\/ either \"A\" or \"D\", stability is not guaranteed.<\/code><\/pre>\n<p>Credit to <a href=\"https:\/\/github.com\/pgolebiowski\">Patryk Golebiowski<\/a>.<\/p>\n<h2>Faster handling of structs as Dictionary values<\/h2>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/49388\">CollectionsMarshal.GetValueRef<\/a> is a new <strong>unsafe<\/strong> API that makes updating struct values in Dictionaries faster. The new API is intended for high performance scenarios, not for general purpose use. It returns a <code>ref<\/code> to the struct value, which can then be updated in place with typical techniques.<\/p>\n<p>The following example demonstrates using the new API:<\/p>\n<pre><code class=\"language-cs\">ref MyStruct value = CollectionsMarshal.GetValueRef(dictionary, key);\r\n\/\/ Returns Unsafe.NullRef&lt;TValue&gt;() if it doesn't exist; check using Unsafe.IsNullRef(ref value)\r\nif (!Unsafe.IsNullRef(ref value))\r\n{\r\n    \/\/ Mutate in-place\r\n    value.MyInt++;\r\n}<\/code><\/pre>\n<p>Prior to this change, updating <code>struct<\/code> dictionary values could be expensive for high-performance scenarios, requiring a dictionary lookup and a copy to stack of the <code>struct<\/code>. Then after changing the <code>struct<\/code>, it would be assigned to the dictionary key again resulting in another look up and copy operation. This improvement reduces the key hashing to 1 (from 2) and removes all the struct copy operations.<\/p>\n<p>Credit to <a href=\"https:\/\/github.com\/benaadams\">Ben Adams<\/a>.<\/p>\n<h3>New <code>DateOnly<\/code> and <code>TimeOnly<\/code> structs<\/h3>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/49036\">Date- and time-only structs<\/a> have been added, with the following characteristics:<\/p>\n<ul>\n<li>Each represent one half of a <code>DateTime<\/code>, either only the date part, or only the time part.<\/li>\n<li><code>DateOnly<\/code> is ideal for birthdays, anniversary days, and business days. It aligns with SQL Server&#8217;s <code>date<\/code> type.<\/li>\n<li><code>TimeOnly<\/code> is ideal for recurring meetings, alarm clocks, and weekly business hours. It aligns with SQL Server&#8217;s <code>time<\/code> type.<\/li>\n<li>Complements existing date\/time types (<code>DateTime<\/code>, <code>DateTimeOffset<\/code>, <code>TimeSpan<\/code>, <code>TimeZoneInfo<\/code>).<\/li>\n<li>In <code>System<\/code> namespace, shipped in CoreLib, just like existing related types.<\/li>\n<\/ul>\n<h3>Perf improvements to <code>DateTime.UtcNow<\/code><\/h3>\n<p>This <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/50263\">improvement<\/a> has the following benefits:<\/p>\n<ul>\n<li>Fixes <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/13091\">2.5x perf regression<\/a> for getting the system time on Windows.<\/li>\n<li>Utilizes a 5-minute sliding cache of Windows leap second data instead of fetching with every call.<\/li>\n<\/ul>\n<h3>Support for both Windows and IANA time zones on all platforms<\/h3>\n<p>This improvement has the following benefits:<\/p>\n<ul>\n<li>Implicit conversion when using <code>TimeZoneInfo.FindSystemTimeZoneById<\/code> (https:\/\/github.com\/dotnet\/runtime\/pull\/49412)<\/li>\n<li>Explicit conversion through new APIs on <code>TimeZoneInfo<\/code>: <code>TryConvertIanaIdToWindowsId<\/code>, <code>TryConvertWindowsIdToIanaId<\/code>, and <code>HasIanaId<\/code> (https:\/\/github.com\/dotnet\/runtime\/issues\/49407)<\/li>\n<li>Improves cross-plat support and interop between systems that use different time zone types.<\/li>\n<li>Removes need to use TimeZoneConverter OSS library. The functionality is now built-in.<\/li>\n<\/ul>\n<h3>Improved time zone display names<\/h3>\n<p><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/48931\">Time zone display names have been improved on Unix<\/a>:<\/p>\n<ul>\n<li>Removes ambiguity from the display names in the list returned by <code>TimeZoneInfo.GetSystemTimeZones<\/code>.<\/li>\n<li>Leverages ICU \/ CLDR globalization data.<\/li>\n<li>Unix only for now. Windows still uses the registry data. This may be changed later.<\/li>\n<\/ul>\n<p>The following additional improvements were also made:<\/p>\n<ul>\n<li>The UTC time zone&#8217;s display name and standard name were hardcoded to English and now uses the same language as the rest of the time zone data (<code>CurrentUICulture<\/code> on Unix, OS default language on Windows).<\/li>\n<li>Time zone display names in Wasm use the non-localized IANA ID instead, due to size limitations.<\/li>\n<li><code>TimeZoneInfo.AdjustmentRule<\/code> nested class gets its <code>BaseUtcOffsetDelta<\/code> internal property made public and gets a new constructor that takes <code>baseUtcOffsetDelta<\/code> as a parameter. (https:\/\/github.com\/dotnet\/runtime\/issues\/50256)<\/li>\n<li><code>TimeZoneInfo.AdjustmentRule<\/code> also gets miscellaneous fixes for loading time zones on Unix (https:\/\/github.com\/dotnet\/runtime\/pull\/49733), (https:\/\/github.com\/dotnet\/runtime\/pull\/50131)<\/li>\n<\/ul>\n<h3>Improved support for Windows ACLs<\/h3>\n<p><a href=\"https:\/\/www.nuget.org\/packages\/System.Threading.AccessControl\/\">System.Threading.AccessControl<\/a> now includes <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/43134\/\">improved support for interacting with Windows access control lists (ACLs)<\/a>. New overloads were added to the <code>OpenExisting<\/code> and <code>TryOpenExisting<\/code> methods for <code>EventWaitHandle<\/code>, <code>Mutex<\/code> and <code>Semaphore<\/code>. These overloads &#8212; with &#8220;security rights&#8221; instances &#8212; enable opening existing instances of threading synchronization objects that were created with special Windows security attributes.<\/p>\n<p>This update matches APIs available in .NET Framework and has the same behavior.<\/p>\n<p>The following examples demonstrate using these new APIs.<\/p>\n<p>For <code>Mutex<\/code>:<\/p>\n<pre><code class=\"language-cs\">var rights = MutexRights.FullControl;\r\nstring mutexName = \"MyMutexName\";\r\n\r\nvar security = new MutexSecurity();\r\nSecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);\r\nMutexAccessRule accessRule = new MutexAccessRule(identity, rights, AccessControlType.Allow);\r\nsecurity.AddAccessRule(accessRule);\r\n\r\n\/\/ createdMutex, openedMutex1 and openedMutex2 point to the same mutex\r\nMutex createdMutex = MutexAcl.Create(initiallyOwned: true, mutexName, out bool createdNew, security);\r\nMutex openedMutex1 = MutexAcl.OpenExisting(mutexName, rights);\r\nMutexAcl.TryOpenExisting(mutexName, rights, out Mutex openedMutex2);<\/code><\/pre>\n<p>For <code>Semaphore<\/code><\/p>\n<pre><code class=\"language-cs\">var rights = SemaphoreRights.FullControl;\r\nstring semaphoreName = \"MySemaphoreName\";\r\n\r\nvar security = new SemaphoreSecurity();\r\nSecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);\r\nSemaphoreAccessRule accessRule = new SemaphoreAccessRule(identity, rights, AccessControlType.Allow);\r\nsecurity.AddAccessRule(accessRule);\r\n\r\n\/\/ createdSemaphore, openedSemaphore1 and openedSemaphore2 point to the same semaphore\r\nSemaphore createdSemaphore = SemaphoreAcl.Create(initialCount: 1,  maximumCount: 3, semaphoreName, out bool createdNew, security);\r\nSemaphore openedSemaphore1 = SemaphoreAcl.OpenExisting(semaphoreName, rights);\r\nSemaphoreAcl.TryOpenExisting(semaphoreName, rights, out Semaphore openedSemaphore2);<\/code><\/pre>\n<p>For <code>EventWaitHandle<\/code><\/p>\n<pre><code class=\"language-cs\">var rights = EventWaitHandleRights.FullControl;\r\nstring eventWaitHandleName = \"MyEventWaitHandleName\";\r\n\r\nvar security = new EventWaitHandleSecurity();\r\nSecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);\r\nEventWaitHandleAccessRule accessRule = new EventWaitHandleAccessRule(identity, rights, AccessControlType.Allow);\r\nsecurity.AddAccessRule(accessRule);\r\n\r\n\/\/ createdHandle, openedHandle1 and openedHandle2 point to the same event wait handle\r\nEventWaitHandle createdHandle = EventWaitHandleAcl.Create(initialState: true, EventResetMode.AutoReset, eventWaitHandleName, out bool createdNew, security);\r\nEventWaitHandle openedHandle1 = EventWaitHandleAcl.OpenExisting(eventWaitHandleName, rights);\r\nEventWaitHandleAcl.TryOpenExisting(eventWaitHandleName, rights, out EventWaitHandle openedHandle2);<\/code><\/pre>\n<h3>HMAC one-shot methods<\/h3>\n<p>The <code>System.Security.Cryptography<\/code> <a href=\"https:\/\/en.wikipedia.org\/wiki\/HMAC\">HMAC<\/a> classes now have <a href=\"https:\/\/github.com\/dotnet\/core\/issues\/6569#issuecomment-913876347\">static methods that allow one-shot calculation of HMACs<\/a> without allocations. These additions are similar to the <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/17590\">one-shot methods for hash generation<\/a> that were added in a previous release.<\/p>\n<h2><code>DependentHandle<\/code> is now public<\/h2>\n<p>The <code>DependentHandle<\/code> type is now public with <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/54246\">the following API surface<\/a>:<\/p>\n<pre><code class=\"language-csharp\">namespace System.Runtime\r\n{\r\n    public struct DependentHandle : IDisposable\r\n    {\r\n        public DependentHandle(object? target, object? dependent);\r\n        public bool IsAllocated { get; }\r\n        public object? Target { get; set; }\r\n        public object? Dependent { get; set; }\r\n        public (object? Target, object? Dependent) TargetAndDependent { get; }\r\n        public void Dispose();\r\n    }\r\n}<\/code><\/pre>\n<p>It can be used by to create advanced systems, such as sophisticated caching systems or customized versions of the <code>ConditionalWeakTable&lt;TKey, TValue&gt;<\/code> type. For instance, it will be used by the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/microsoft.toolkit.mvvm.Messaging.WeakReferenceMessenger\"><code>WeakReferenceMessenger<\/code><\/a> type in the <a href=\"https:\/\/aka.ms\/mvvmtoolkit\/docs\">MVVM Toolkit<\/a> to avoid memory allocations when broadcasting messages.<\/p>\n<h3>Portable thread pool<\/h3>\n<p>The <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/38225\">.NET thread pool has been re-implemented<\/a> as a managed implementation and is now used as the default thread pool in .NET 6. We made this change to enable all .NET applications to have access to the same thread pool independent of whether the CoreCLR, Mono, or any other runtime was being used. We have not observed or expect any functional or performance impact as part of this change.<\/p>\n<h2>RyuJIT<\/h2>\n<p>The team has made many improvements to the .NET JIT compiler this release, documented in each of the preview posts. Most of these changes improve performance. A few of the RyuJIT highlights are covered here.<\/p>\n<h3>Dynamic PGO<\/h3>\n<p>In .NET 6 we&#8217;ve enabled two forms of PGO (Profile Guided Optimization):<\/p>\n<ul>\n<li><strong>Dynamic PGO<\/strong> uses data gathered from the current run to optimize the current run.<\/li>\n<li><strong>Static PGO<\/strong> relies on data gathered from past runs to optimize future runs.<\/li>\n<\/ul>\n<p>Dynamic PGO was already covered in the performance section, earlier in the post. I&#8217;ll provide a re-cap.<\/p>\n<p>Dynamic PGO enables the JIT to collect information at runtime about the code paths and types that are actually used for that specific run of the app. The JIT can then optimize the code in terms of those code paths for sometimes very significantly improved performance. We&#8217;ve seen healthy double-digit improvements in both testing and production. There are a set of classic compiler techniques that are not possible with either a JIT or ahead-of-time compilation without PGO. We&#8217;re now able to apply those techniques. Hot\/cold splitting is one such technique and devirtualization is another.<\/p>\n<p>To enable Dynamic PGO, set <code>DOTNET_TieredPGO=1<\/code> in the environment where your application will run.<\/p>\n<p>As stated in the performance section, dynamic PGO delivers a 26% improvement (510K -&gt; 640K) in requests per second for the TechEmpower JSON &#8220;MVC&#8221; suite. That&#8217;s an amazing improvement with no code changes.<\/p>\n<p>Our ambition is to enable Dynamic PGO by default in a future release of .NET, hopefully with .NET 7. We strongly encourage you to try Dynamic PGO in your applications and give us feedback.<\/p>\n<h3>Full PGO<\/h3>\n<p>To get the full benefit of Dynamic PGO, you can set two extra environment variables: <code>DOTNET_TC_QuickJitForLoops=1<\/code> and <code>DOTNET_ReadyToRun=0<\/code>. This ensures that as many methods as possible participate in tiered compilation. We call this variant <strong>Full PGO<\/strong>. Full PGO can provide larger steady-state performance benefits than Dynamic PGO but will have slower startup times (since more methods must be jitted at Tier 0).<\/p>\n<p>You would not want to use this option for a short-running serverless application, but it could make sense for a long-running one.<\/p>\n<p>In future releases we plan to streamline and simplify these options so that you can get the benefits of full PGO more simply and for a wider range of applications.<\/p>\n<h4>Static PGO<\/h4>\n<p>We currently use <strong>Static PGO<\/strong> to optimize .NET Libraries assemblies like <code>System.Private.CoreLib<\/code> that ship with R2R (Ready To Run).<\/p>\n<p>The benefit of static PGO is that is that optimizations are made when assemblies are compiled to R2R format with crossgen. That means that there is runtime benefit with no runtime cost. That&#8217;s very significant and the reason why PGO is important with C++, for example.<\/p>\n<h3>Loop alignment<\/h3>\n<p>Memory alignment is a common requirement for various operations in modern computing. In .NET 5, we started <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/42909\">aligning methods at the 32-byte boundary<\/a>. In .NET 6, we have added a feature to perform <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/loop-alignment-in-net-6\/\">adaptive loop alignment<\/a> that adds <code>NOP<\/code> padding instructions in a method having loops such that the loop code starts at mod(16) or mod(32) memory address. These changes improve and stabilize the performance of .NET code.<\/p>\n<p>In the following <a href=\"https:\/\/github.com\/dotnet\/performance\/blob\/main\/src\/benchmarks\/micro\/runtime\/Benchstones\/BenchI\/BubbleSort2.cs\">Bubble sort<\/a> chart, data point 1 represents the point where we started aligning methods at 32-byte boundary. Data point 2 represents the point where we started aligning inner loops that as well. As you can see, both the performance and stability of the benchmark improve considerably.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2021\/11\/stable-bubble.png\" alt=\"bubble sort chart\" \/><\/p>\n<h3>Hardware-accelerated structs<\/h3>\n<p>Structs are an important part of the CLR type system. In recent years, they have been frequently used as a performance primitive throughout the .NET libraries. Recent examples are <code>ValueTask<\/code>, <code>ValueTuple<\/code> and <code>Span&lt;T&gt;<\/code>. Record structs are a new example. In .NET 5 and .NET 6, we&#8217;ve been improving performance for structs, in part by <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/43867\">ensuring that structs can be held in ultra-fast CPU registers<\/a> when they are locals, arguments or return values of methods). This is particularly beneficial for APIs that compute with vectors.<\/p>\n<h3>Stabilize performance measurements<\/h3>\n<p>There is a tremendous amount of engineering systems work on the team that never appears on the blog. That will be true for any hardware or software product you use. The JIT team took on a project to <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/43227\">stabilize performance measurements<\/a> with the goal of increasing the value of regressions that are auto-reported by our internal performance lab automation. This project is interesting because of the <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/43227\">in-depth investigation<\/a> and the <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/44370\">product changes<\/a> that were required to enable stability. It also demonstrates the scale at which we measure to maintain and improve performance.<\/p>\n<p><img decoding=\"async\" class=\"alignnone\" src=\"https:\/\/user-images.githubusercontent.com\/63486087\/106191620-f8452300-615f-11eb-934d-e00495992f1d.png\" alt=\"perf-#43227, Announcing .NET 6 \u2014 The Fastest .NET Yet\" width=\"868\" height=\"351\" \/><\/p>\n<p>This image demonstrates unstable performance measurements where performance fluctuated between a slow and fast in successive runs. The x-axis is the date of the test and y-axis is the time of the test in nanoseconds. By the end of the chart (after these changes were committed), you can see that measurements stabilized, with the best result. This image demonstrates a single test. There are many more tests that are demonstrated to have similar behavior at <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/43227\">dotnet\/runtime #43227<\/a>.<\/p>\n<h2>Ready-to-run Code \/ Crossgen 2<\/h2>\n<p>Crossgen2 is a replacement of the <a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/main\/docs\/workflow\/building\/coreclr\/crossgen.md\">crossgen tool<\/a>. It is intended to satisfy two outcomes:<\/p>\n<ul>\n<li>Make crossgen development more efficient.<\/li>\n<li>Enable a set of capabilities that are not currently possible with crossgen.<\/li>\n<\/ul>\n<p>This transition is somewhat similar to the native code csc.exe to managed-code <a href=\"https:\/\/github.com\/dotnet\/roslyn\">Roslyn compiler<\/a>. Crossgen2 is written in C#, however, it doesn&#8217;t expose a fancy API like Roslyn does.<\/p>\n<p>There are perhaps a half-dozen projects we had\/have planned for .NET 6 and 7 that are dependent on crossgen2. The <a href=\"https:\/\/github.com\/dotnet\/designs\/pull\/173\">proposal for vector instruction default<\/a> is a great example of a crossgen2 capability and product change that we wanted to make for .NET 6 but is more likely .NET 7. <a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/main\/docs\/design\/features\/crossgen2-compilation-structure-enhancements.md\">Version bubbles<\/a> are another good example.<\/p>\n<p>Crossgen2 enables cross-compilation (hence the name &#8220;crossgen&#8221;) across operating system and architecture dimensions. That means that you will be able to use a single build machine to generate native code for all targets, at least as it relates to ready-to-run code. Running and testing that code is a different story, however, and you&#8217;ll need appropriate hardware and operating systems for that.<\/p>\n<p>The first step is to compile the platform itself with crossgen2. We completed that with .NET 6, for all architectures. As a result, we were able to retire the old crossgen this release. Note that crossgen2 only applies to CoreCLR and not to Mono-based applications (which have a separate set of code generation tools).<\/p>\n<p>This project &#8212; at least at first &#8212; is not oriented on performance. The goal is to enable a much better architecture for hosting the RyuJIT (or any other) compiler to generate code in an offline manner (not requiring or starting the runtime).<\/p>\n<p>You might say &#8220;hey &#8230; don&#8217;t you have to start the runtime to run crossgen2 if it is written in C#?&#8221; Yes, but that&#8217;s not what is meant by &#8220;offline&#8221; in this context. When crossgen2 runs, we&#8217;re not using the JIT that comes with the runtime that crossgen2 is running on to generate <a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/main\/docs\/design\/coreclr\/botr\/readytorun-format.md\">ready-to-run (R2R) code<\/a>. That won&#8217;t work, at least not with the goals we have. Imagine crossgen2 is running on an x64 machine, and we need to generate code for Arm64. Crossgen2 loads the Arm64 RyuJIT &#8212; compiled for x64 &#8212; as a native plugin, and then uses it to generate Arm64 R2R code. The machine instructions are just a stream of bytes that are saved to a file. It can also work in the opposite direction. On Arm64, crossgen2 can generate x64 code using the x64 RyuJIT compiled to Arm64. We use the same approach to target x64 code on x64 machines. Crossgen2 loads a RyuJIT built for whatever configuration is needed. That may seem complicated, but it&#8217;s the sort of system you need in place if you want to enable a seamless cross-targeting model, and that&#8217;s exactly what we want.<\/p>\n<p>We hope to use the term &#8220;crossgen2&#8221; for just one release, after which it will replace the existing crossgen, and then we&#8217;ll go back to using the &#8220;crossgen&#8221; term for &#8220;crossgen2&#8221;.<\/p>\n<h2>.NET Diagnostics: EventPipe<\/h2>\n<p>EventPipe is our cross-platform mechanism for egressing events, performance data, and counters, either in-process or out-of-process. Starting with .NET 6, we&#8217;ve moved the implementation from C++ to C. With this change, Mono uses EventPipe as well. This means that both CoreCLR and Mono use the same eventing infrastructure, including the .NET Diagnostics CLI Tools.<\/p>\n<p>This change also came with small reduction in size for CoreCLR:<\/p>\n<table>\n<thead>\n<tr>\n<th>lib<\/th>\n<th>after size &#8211; before size<\/th>\n<th>diff<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>libcoreclr.so<\/td>\n<td>7037856 &#8211; 7049408<\/td>\n<td>-11552<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We&#8217;ve also made some <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/45518\">changes that improve EventPipe throughput while under load<\/a>. Over the first few previews, we&#8217;ve made a series of changes that result in throughput improvements as high as 2.06x what .NET 5 was capable of:<\/p>\n<p><img decoding=\"async\" class=\"alignnone\" src=\"https:\/\/user-images.githubusercontent.com\/20667293\/119393587-00cce680-bc86-11eb-87a0-2d6b9a575f84.png\" alt=\"Announcing .NET 6 \u2014 The Fastest .NET Yet\" width=\"2394\" height=\"1810\" \/><\/p>\n<p>Higher is better for this benchmark. .NET 6 is the orange line and .NET 5 is the blue one.<\/p>\n<h2>SDK<\/h2>\n<p>The following improvements were made to the .NET SDK.<\/p>\n<h3>CLI install of .NET 6 SDK Optional Workloads<\/h3>\n<p>.NET 6 introduces the concept of <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2020\/workloads\/workloads.md\">SDK workloads<\/a>. Workload are optional components can be installed on top of the .NET SDK to enable various scenarios. The new workloads in .NET 6 are: .NET MAUI and Blazor WebAssembly AOT workloads. We will likely create new workloads (possibly from the existing SDK) in .NET 7. The biggest benefit of workloads is size reduction and optionality. We want to make the SDK smaller over time and enable installing just the components you need. This model will be good for developer machines and even better for CI.<\/p>\n<p>Visual Studio users don&#8217;t really need to worry about workloads. The workloads feature has been designed so that an installation orchestrator like Visual Studio can install workloads for you. Workloads can be managed directly via the CLI.<\/p>\n<p>The workloads feature exposes multiple verbs for managing workloads, including the following ones:<\/p>\n<ul>\n<li><code>dotnet workload restore<\/code> &#8212; installs the workloads required by a given project.<\/li>\n<li><code>dotnet workload install<\/code> &#8212; installs a named workload.<\/li>\n<li><code>dotnet workload list<\/code> &#8212; lists the workloads you have installed.<\/li>\n<li><code>dotnet workload update<\/code> &#8212; updates all installed workloads to the newest available version.<\/li>\n<\/ul>\n<p>The <code>update<\/code> verb queries <code>nuget.org<\/code> for updated workload manifests, updates local manifests, downloads new versions of the installed workloads, and then removes all old versions of a workload. This is analogous to <code>apt update &amp;&amp; apt upgrade -y<\/code> (used on Debian-based Linux distros). It is reasonable to think of workloads as a private package manager for the SDK. It is private in the sense that it is only available for SDK components. We may reconsider that in future.<\/p>\n<p>The <code>dotnet workload<\/code> commands operate in the context of the given SDK. Imagine you have both .NET 6 and .NET 7 installed. The workloads commands will provide different results for each SDK since the workloads will be different (at least different versions of the same workloads).<\/p>\n<p>Note that <code>dotnet workload install<\/code> copies the workloads from NuGet.org into your SDK install so will need to be run elevated or use <code>sudo<\/code> if the SDK install location is protected (meaning at an admin\/root location).<\/p>\n<h3>Built-in SDK version checking<\/h3>\n<p>To make it easier to track when new versions of the SDK and Runtimes are available, we\u2019ve added a new command to the .NET 6 SDK.<\/p>\n<pre><code class=\"language-bash\">dotnet sdk check<\/code><\/pre>\n<p>It tells you if there is a newer version available for any of the .NET SDKs, runtimes, or workloads you have installed. You can see the new experience in the following image.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/12663534\/119046999-374aee80-b972-11eb-8967-2111b7e7a586.png\" alt=\"sdkcheck\" \/><\/p>\n<h3><code>dotnet new<\/code><\/h3>\n<p>You can now search NuGet.org for new templates with <code>dotnet new --search<\/code>.<\/p>\n<p>Other improvements to template installation include support for the <code>--interactive<\/code> switch to support authorization credentials for private NuGet feeds.<\/p>\n<p>Once CLI templates are installed, you can check if updates are available via <code>--update-check<\/code> and <code>--update-apply<\/code>.<\/p>\n<h3>NuGet Package Validation<\/h3>\n<p><a href=\"https:\/\/docs.microsoft.com\/dotnet\/fundamentals\/package-validation\/overview\">Package Validation tools<\/a> enables NuGet library developers to validate that their packages are consistent and well-formed.<\/p>\n<p>This includes:<\/p>\n<ul>\n<li>Validate that there are no breaking changes across versions.<\/li>\n<li>Validate that the package has the same set of publics APIs for all runtime-specific implementations.<\/li>\n<li>Determine any target-framework- or runtime- applicability gaps.<\/li>\n<\/ul>\n<p>This tool is part of the SDK. The easiest way to use it is by setting a new property in your project file.<\/p>\n<pre class=\"prettyprint\">&lt;EnablePackageValidation&gt;true&lt;\/EnablePackageValidation&gt;<\/pre>\n<h3>More Roslyn Analyzers<\/h3>\n<p>In .NET 5, we shipped approximately 250 analyzers with the .NET SDK. Many of them already existed but were shipped out-of-band as NuGet packages. We&#8217;ve <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/43617\">adding more analyzers for .NET 6<\/a>.<\/p>\n<p>By default, most of the new analyzers are enabled at Info level. You can enable these analyzers at Warning level by <a href=\"https:\/\/docs.microsoft.com\/dotnet\/fundamentals\/code-analysis\/overview#enable-additional-rules\">configuring the analysis mode<\/a> like this: <code>&lt;AnalysisMode&gt;All&lt;\/AnalysisMode&gt;<\/code>.<\/p>\n<p>We published the set of analyzers we wanted for .NET 6 (plus some extras) and then made most of them <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues?q=label%3Aup-for-grabs+label%3Acode-fixer+label%3Acode-analyzer\">up-for-grabs<\/a>. The community has added several implementations, including these ones.<\/p>\n<table>\n<thead>\n<tr>\n<th>Contributor<\/th>\n<th>Issue<\/th>\n<th>Title<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/github.com\/NewellClark\">Newell Clark<\/a><\/td>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/33777\">dotnet\/runtime #33777<\/a><\/td>\n<td>Use span-based <code>string.Concat<\/code><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/NewellClark\">Newell Clark<\/a><\/td>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/33784\">dotnet\/runtime #33784<\/a><\/td>\n<td>Prefer <code>string.AsSpan()<\/code> over <code>string.Substring()<\/code> when parsing<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/NewellClark\">Newell Clark<\/a><\/td>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/33789\">dotnet\/runtime #33789<\/a><\/td>\n<td>Override <code>Stream.ReadAsync\/WriteAsync<\/code><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/NewellClark\">Newell Clark<\/a><\/td>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/35343\">dotnet\/runtime #35343<\/a><\/td>\n<td>Replace <code>Dictionary&lt;,&gt;.Keys.Contains<\/code> with <code>ContainsKey<\/code><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/NewellClark\">Newell Clark<\/a><\/td>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/45552\">dotnet\/runtime #45552<\/a><\/td>\n<td>Use <code>String.Equals<\/code> instead of <code>String.Compare<\/code><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/MeikTranel\">Meik Tranel<\/a><\/td>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/47180\">dotnet\/runtime #47180<\/a><\/td>\n<td>Use <code>String.Contains(char)<\/code> instead of <code>String.Contains(String)<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Thanks <a href=\"https:\/\/github.com\/MeikTranel\">Meik Tranel<\/a> and <a href=\"https:\/\/github.com\/NewellClark\">Newell Clark<\/a>.<\/p>\n<h3>Enable custom guards for Platform Compatibility Analyzer<\/h3>\n<p><a href=\"https:\/\/docs.microsoft.com\/dotnet\/standard\/analyzers\/platform-compat-analyzer\">The CA1416 Platform Compatibility analyzer<\/a> already recognizes platform guards using the methods in<code>OperatingSystem<\/code> and <code>RuntimeInformation<\/code>, such as <code>OperatingSystem.IsWindows<\/code> and <code>OperatingSystem.IsWindowsVersionAtLeast<\/code>. However, <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/44922\">the analyzer does not recognize any other guard possibilities<\/a> like the platform check result cached in a field or property, or complex platform check logic is defined in a helper method.<\/p>\n<p>For <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/51541\">allowing custom guard possibilities<\/a> we <a href=\"https:\/\/github.com\/dotnet\/roslyn-analyzers\/pull\/5087\">added new attributes<\/a> <code>SupportedOSPlatformGuard<\/code> and <code>UnsupportedOSPlatformGuard<\/code> for annotating the custom guard members with the corresponding platform name and\/or version. This annotation is recognized and respected by the Platform Compatibility analyzer&#8217;s flow analysis logic.<\/p>\n<h3>Usage<\/h3>\n<pre><code class=\"language-cs\">    [UnsupportedOSPlatformGuard(\"browser\")] \/\/ The platform guard attribute\r\n#if TARGET_BROWSER\r\n    internal bool IsSupported =&gt; false;\r\n#else\r\n    internal bool IsSupported =&gt; true;\r\n#endif\r\n\r\n    [UnsupportedOSPlatform(\"browser\")]\r\n    void ApiNotSupportedOnBrowser() { }\r\n\r\n    void M1()\r\n    {\r\n        ApiNotSupportedOnBrowser();  \/\/ Warns: This call site is reachable on all platforms.'ApiNotSupportedOnBrowser()' is unsupported on: 'browser'\r\n\r\n        if (IsSupported)\r\n        {\r\n            ApiNotSupportedOnBrowser();  \/\/ Not warn\r\n        }\r\n    }\r\n\r\n    [SupportedOSPlatform(\"Windows\")]\r\n    [SupportedOSPlatform(\"Linux\")]\r\n    void ApiOnlyWorkOnWindowsLinux() { }\r\n\r\n    [SupportedOSPlatformGuard(\"Linux\")]\r\n    [SupportedOSPlatformGuard(\"Windows\")]\r\n    private readonly bool _isWindowOrLinux = OperatingSystem.IsLinux() || OperatingSystem.IsWindows();\r\n\r\n    void M2()\r\n    {\r\n        ApiOnlyWorkOnWindowsLinux();  \/\/ This call site is reachable on all platforms.'ApiOnlyWorkOnWindowsLinux()' is only supported on: 'Linux', 'Windows'.\r\n\r\n        if (_isWindowOrLinux)\r\n        {\r\n            ApiOnlyWorkOnWindowsLinux();  \/\/ Not warn\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<h2>Closing<\/h2>\n<p>Welcome to .NET 6. It is another huge .NET release, with near-equal servings of performance, functionality, usability, and security improvements. We hope you find many improvements that end up making you more efficient and capable in your every-day development and incease performance or reduce the costs of your apps in production. We&#8217;ve already started hearing good things from those among you have already started to use .NET 6.<\/p>\n<p>At Microsoft, we&#8217;re also in the early phases of .NET 6 deployment, with a few key apps already in production and many more soon to come in the coming weeks and months.<\/p>\n<p>.NET 6 is our latest LTS release. We encourage everyone to move to it, particularly if you are using .NET 5. We&#8217;re expecting it to be the fastest adopted .NET release ever.<\/p>\n<p>This release is the result of at least 1000 people (but probably a lot more). That includes .NET team from Microsoft and many more in the community. I&#8217;ve tried to include many community-contributed features in this post. Thank you for taking the time to create those and work through our process. I hope that the experience was a good one and that even more people will contribute.<\/p>\n<p>This post is the result of <a href=\"https:\/\/twitter.com\/dsymetweets\/status\/1457494213994684416\">a collaboration of many talented people<\/a>. The contributions includes the <a href=\"https:\/\/github.com\/dotnet\/core\/issues\/6570\">feature content<\/a> that the team provides throughout the release, significant new content created for this final post, and a ton of technical and prose corrections that are required to bring the final content up to the quality you deserve. It&#8217;s been a pleasure crafting it and all the other posts for you.<\/p>\n<p>Thanks for being a .NET developer.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>.NET 6 is now available. It is easier to use, runs faster, and has many new features.<\/p>\n","protected":false},"author":1312,"featured_media":37430,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,7254],"tags":[],"class_list":["post-37429","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-featured"],"acf":[],"blog_post_summary":"<p>.NET 6 is now available. It is easier to use, runs faster, and has many new features.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/37429","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/1312"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=37429"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/37429\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/37430"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=37429"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=37429"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=37429"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}