Announcing .NET Core 3.0 Preview 6

Rich Lander [MSFT]

Today, we are announcing .NET Core 3.0 Preview 6. It includes updates for compiling assemblies for improved startup, optimizing applications for size with linker and EventPipe improvements. We’ve also released new Docker images for Alpine on ARM64.

Download .NET Core 3.0 Preview 6 right now on Windows, macOS and Linux.

Release notes have been published at dotnet/core. An API diff between Preview 5 and 6 is also available.

ASP.NET Core and EF Core are also releasing updates today.

If you missed it, check out the improvements we released in .NET Core 3.0 Preview 5, from last month.

WPF and Windows Forms update

The WPF team has now completed publishing most of the WPF codebase to GitHub. In fact, they just published source for fifteen assemblies. For anyone familiar with WPF, the assembly names should be very familiar.

In some cases, tests are still on the backlog to get published at or before 3.0 GA. That said, the presence of all of this code should enable the WPF community to fully participate in making changes across WPF. It is obvious from reading some of the GitHub issues that the community has its own backlog that it has been waiting to realize. Dark theme, maybe?

Alpine Docker images

Docker images are now available for both .NET Core and ASP.NET Core on ARM64. They were previously only available for x64.

The following images can be used in a Dockerfile, or with docker pull, as demonstrated below:

  • docker pull mcr.microsoft.com/dotnet/core/runtime:3.0-alpine-arm64v8
  • docker pull mcr.microsoft.com/dotnet/core/aspnet:3.0-alpine-arm64v8

Event Pipe improvements

Event Pipe now supports multiple sessions. This means that you can consume events with EventListener in-proc and simultaneously have out-of-process event pipe clients.

New Perf Counters added:

  • % Time in GC
  • Gen 0 Heap Size
  • Gen 1 Heap Size
  • Gen 2 Heap Size
  • LOH Heap Size
  • Allocation Rate
  • Number of assemblies loaded
  • Number of ThreadPool Threads
  • Monitor Lock Contention Rate
  • ThreadPool Work Items Queue
  • ThreadPool Completed Work Items Rate

Profiler attach is now implemented using the same Event Pipe infrastructure.

See Playing with counters from David Fowler to get an idea of what you can do with event pipe to perform your own performance investigations or just monitor application status.

See dotnet-counters to install the dotnet-counters tool.

Optimize your .NET Core apps with ReadyToRun images

You can improve the startup time of your .NET Core application by compiling your application assemblies as ReadyToRun (R2R) format. R2R is a form of ahead-of-time (AOT) compilation.

R2R binaries improve startup performance by reducing the amount of work the JIT needs to do as your application is loading. The binaries contain similar native code as what the JIT would produce, giving the JIT a bit of a vacation when performance matters most (at startup). R2R binaries are larger because they contain both intermediate language (IL) code, which is still needed for some scenarios, and the native version of the same code, to improve startup.

R2R is supported with .NET Core 3.0. It cannot be used with earlier versions of .NET Core.

Sample performance numbers

The following are performance numbers collected using a sample WPF application. The application was published as self-contained and did not use the assembly linker (covered later this post).

IL-only Application:

  • Startup time: 1.9 seconds
  • Memory usage: 69.1 MB
  • Application size: 150 MB

With ReadyToRun images:

  • Startup time: 1.3 seconds.
  • Memory usage: 55.7 MB
  • Application size: 156 MB

ReadyToRun images, explained

You can R2R compile both libraries and application binaries. At present, libraries can only be R2R compiled as part of an application, not for delivery as a NuGet package. We’d like more feedback on whether that scenario is important.

AOT compiling assemblies has been available as a concept with .NET for a long time, going back to the .NET Framework and NGEN. NGEN has a key drawback, which is that compilation must be done on client machines, using the NGEN tool. It isn’t possible to generate NGEN images as part of your application build.

Enter .NET Core. It comes with crossgen, which produces native images in a newer format called ReadyToRun. The name describes its primary value proposition, which is that these native images can be built as part of your build and are “ready to run” without any additional work on client machines. That’s a major improvement, and also an important win for climate change.

In terms of compatibility, ReadyToRun images are similar to IL assemblies, with some key differences.

  • IL assemblies contain just IL code. They can run on any runtime that supports the given target framework for that assembly. For example a netstandard2.0 assembly can run on .NET Framework 4.6+ and .NET Core 2.0+, on any supported operating system (Windows, macOS, Linux) and architecture (Intel, ARM, 32-bit, 64-bit).
  • R2R assemblies contain IL and native code. They are compiled for a specific minimum .NET Core runtime version and runtime environment (RID). For example, a netstandard2.0 assembly might be R2R compiled for .NET Core 3.0 and Linux x64. It will only be usable in that or a compatible configuration (like .NET Core 3.1 or .NET Core 5.0, on Linux x64), because it contains native code that is only usable in that runtime environment.

Instructions

The ReadyToRun compilation is a publish-only, opt-in feature. We’ve released a preview version of it with .NET Core 3.0 Preview 5.

To enable the ReadyToRun compilation, you have to:

  • Set the PublishReadyToRun property to true.
  • Publish using an explicit RuntimeIdentifier.

Note: When the application assemblies get compiled, the native code produced is platform and architecture specific (which is why you have to specify a valid RuntimeIdentifier when publishing).

Here’s an example:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <PublishReadyToRun>true</PublishReadyToRun>
  </PropertyGroup>
</Project>

And publish using the following command:

dotnet publish -r win-x64 -c Release

Note: The RuntimeIdentifier can also be set in the project file.

Note: ReadyToRun is currently only supported for self-contained apps. It will be enabled for framework-dependent apps in a later preview.

Native symbol generation can be enabled by setting the PublishReadyToRunEmitSymbols property to true in your project. You do not need to generate native symbols for debugging purposes. These symbols are only useful for profiling purposes.

The SDK currently supports a way to exclude certain assemblies from being compiled into ReadyToRun images. This could be useful for cases when certain assemblies do not really need to be optimized for performance. This can help reduce the size of the application. It could also be a useful workaround for cases where the ReadyToRun compiler fails to compile a certain assembly. Exclusion is done using the PublishReadyToRunExclude item group. Example:

<ItemGroup>
  <PublishReadyToRunExclude Include="FilenameOfAssemblyToExclude.dll" />
</ItemGroup>

Cross platform/architecture compilations

The ReadyToRun compiler doesn’t currently support cross-targeting. You need to compile on a given target. For example, if you want R2R images for Windows x64, you need to run the publish command on that environment.

Exceptions to this:

  • Windows x64 can be used to compiles Windows ARM32, ARM64, and x86 images.
  • Windows x86 can be used to compile Windows ARM32 images.
  • Linux x64 can be used to compile Linux ARM32 and ARM64 images.

Assembly linking

The .NET core 3.0 SDK comes with a tool that can reduce the size of apps by analyzing IL and trimming unused assemblies.

With .NET Core, it has always been possible to publish self-contained apps that include everything needed to run your code, without requiring .NET to be installed on the deployment target. In some cases, the app only requires a small subset of the framework to function and could potentially be made much smaller by including only the used libraries.

We use the IL linker to scan the IL of your application to detect which code is actually required, and then trim unused framework libraries. This can significantly reduce the size of some apps. Typically, small tool-like console apps benefit the most as they tend to use fairly small subsets of the framework and are usually more amenable to trimming.

To use this tool, set PublishTrimmed=true in your project and publish a self-contained app:

dotnet publish -r <rid> -c Release

The publish output will include a subset of the framework libraries, depending on what the application code calls. For a helloworld app, the linker reduces the size from ~68MB to ~28MB.

Applications or frameworks (including ASP.NET Core and WPF) that use reflection or related dynamic features will often break when trimmed, because the linker doesn’t know about this dynamic behavior and usually can’t determine which framework types will be required for reflection at run time. To trim such apps, you need to tell the linker about any types needed by reflection in your code, and in any packages or frameworks that you depend on. Be sure to test your apps after trimming.

For more information about the IL Linker, see the documentation, or visit the mono/linker repo.

Note: In previous versions of .NET Core, ILLink.Tasks was shipped as an external NuGet package and provided much of the same functionality. It is no longer supported – please update to the latest 3.0 SDK and try the new experience!

Using the Linker and ReadyToRun Together

The linker and ReadyToRun compiler can be used for the same application. In general, the linker makes your application smaller, and then the ready-to-run compiler will make it a bit larger again, but with a significant performance win. It is worth testing in various configurations to understand the impact of each option.

Note: dotnet/sdk #3257 prevents the linker and ReadyToRun from being used together for WPF and Windows Forms applications. We are working on fixing that as part of the .NET Core 3.0 release.

Native Hosting sample

The team recently posted a Native Hosting sample. It demonstrates a best practice approach for hosting .NET Core in a native application.

As part of .NET Core 3.0, we now expose general functionality to .NET Core native hosts that was previously only available to .NET Core managed applications through the officially provided .NET Core hosts. The functionality is primarily related to assembly loading. This functionality should make it easier to produce native hosts that can take advantage of the full feature set of .NET Core.

HTTP/2 support in HttpClient

HTTP/2 is a major revision of the HTTP protocol. Some of the notable features of HTTP/2 are support for header compression and fully multiplexed streams over the same connection. While HTTP/2 preserves HTTP’s semantics (HTTP headers, methods, etc) it is a change from HTTP/1.x in how data is framed and sent over the wire.

HttpClient now add supports for making HTTP/2 requests. While the default remains HTTP/1.1, you can opt in to using HTTP/2 by setting the version on your HTTP request message.

var client = new HttpClient() { BaseAddress = new Uri("https://localhost:5001") };
// HTTP/1.1 request
using (var response = await client.GetAsync("/"))
{
    Console.WriteLine(response.Content);
}
// HTTP/2 request
using (var request = new HttpRequestMessage(HttpMethod.Get, "/") { Version = new Version(2, 0) })
using (var response = await client.SendAsync(request))
{
    Console.WriteLine(response.Content);
}

Alternatively, you can default to sending HTTP/2 requests by setting the DefaultRequestVersion property on HttpClient.

var client = new HttpClient()
{
    BaseAddress = new Uri("https://localhost:5001"),
    DefaultRequestVersion = new Version(2, 0)
};
// Defaults to HTTP/2
using (var response = await client.GetAsync("/"))
{
    Console.WriteLine(response.Content);
}

As a consequence of this change in framing, servers and clients need to negotiate the protocol version used. Application-Layer Protocol Negotiation (ALPN) is a TLS extension that allows the server and client negotiate the protocol version used as part of their TLS handshake. While it is possible to have prior knowledge between the server and the client on the protocol, most servers only support ALPN as the only way to establish an HTTP/2 connection. As such, HTTP/2 is negotiated by HttpClient only on a TLS connection.

In development scenarios when server and client have a priori knowledge that both will speak HTTP/2 unencrypted, you may establish an HTTP/2 connection over cleartext by setting an AppContext switch or an environment variable (DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2UNENCRYPTEDSUPPORT=1).

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

Closing

Please try out the new features. Please file issues for the bugs or any challenging experiences you find. We want the feedback! You can file feature requests, too, but they likely will need to wait to get implemented until the next release at this point.

We are now getting very close to being feature complete for .NET Core 3.0, and are now transitioning the focus of the team to the quality of the release. We’ve got a few months of bug fixing and performance work ahead. We’ll appreciate your feedback as we work through that process, too.

On that note, we will soon be switching the master branches on .NET Core repos to the next major release, likely at or shortly after the Preview 7 release (July).

Thanks for trying out .NET Core 3.0 previews. We appreciate your help. At this point, we’re focused on getting a final release in your hands.

44 comments

Discussion is closed. Login to edit/delete existing comments.

  • Mike-E 0

    Very nice, team.  .NET is truly where MSFT is at these days.  With Blazor on the way — especially its server-side mode which should be marketed/promoted more — I’m starting to get the warm n’ fuzzies around application development again.  ReadyToRun, indeed. 

      • Mike-E 0

        HAH!  True.  I know I have been hassling you all for DAYS now in regards to a ubiquitous .NET, so I appreciate your patience and for not banhammering me — yet, heh heh.  FWIW, I was not excited about Blazor until finding out about its server-side/WebSocket mode… very innovative approach and perfect for those like me who don’t want to jump fully into the WebAssembly pool just yet.  It’s pretty remarkable to have both WebSockets and WebAssembly provided and available in one solution, and it’s the closest I’ve seen to Silverlight yet, despite having no Xaml.  I’ll take an ugly Silverlight over no Silverlight anytime, however.

        • Per Lundberg 0

          Interesting to hear – I’m also one of the ones missing Silverlight. 🙂 It’s deployment model (browser plugin and all) was obviously the bad part, but being able to bring the goodness of C# and XAML to the web – it wasn’t such a bad idea.

    • James Wil 0

      If they don’t figure out a way to shrink down all the output, Blazor will fail, webasm is still the web, nobody will want fat binaries!! on desktop it is already borderline

  • LOST 0

    I honestly think NGen is in many ways a better solution to native precompilation for two major reasons:
    1. It simplifies my job as a developer: now I can focus on features and high-level performance of my app, no need to bother with building for a myriad of different platforms, which is one of the major points in targeting CLR vs native in the first place.
    2. Client-side NGen can detect additional CPU features like AVX or even specific processor model, and enable related optimizations, yielding better improvements at both startup and runtime.

    • Bert Stomphorst 0

      That’s right, but a major drawback of NGen is that it requires admin-permissions on the client-machine. This isn’t helpful when distributing apps that don’t need admin-permissions for install (i.e. with automatic updates / install with Squirrel.Windows)

      • Richard LanderMicrosoft employee 0

        Correct … NGEN and R2R/crossgen have overlap but have very important differences. Our belief is that very few developers used ngen in practice because it’s hard to use, requires admin, and can significantly extend the installation time for a large app.

        I agree that R2R brings a new set of deployment challenges, although i think the challenges are more in theory than practice. .NET Core EXEs are native code, as well, so present the same CPU/OS requirements as R2R images. We’ve all been spoiled for so many years with IL EXEs on Windows. We don’t have a good way of deliverying that with .NET Core, because it’s not baked into any/all operating systems it supports.

        More specifically, we don’t believe that chip-specific optimimations are a big deal for R2R images, even though that’s unintuitive. With tiered compilation, it’s unlikely that you’d have a scenario where you’d notice the lack of these optimizations in the R2R code.

        • Max Mustermueller 0

          Another thing about ngen is that it doesn’t work if you distribute a portable application because telling users to run ngen on it is pretty much no solution. If you distribute via an installer… well okay ngen might work but in all other cases it doesn’t.
          So if we get the possibility to generate native code ourselves on our machines and distribute it without having to run anything additional on every computer, its a huge benefit.
          I’m really looking forward for the AOT compiler and meanwhile enjoy ready2run.

    • James Wil 0

      I disagree, this ads lot of bloat that you need to ship in every client’s machine
      I’m for a solution that ships only strict minimum, just what is needed to run the said code, static AOT compilation is the only solution
      I don’t understand why they try to avoid it, all path lead to this way, there is no shortcut, see Java and how it miserably failed

  • Ismail Demir 0

    R.I.P VB.Net
    Thank You!

  • bat forest 0

    About 30MB on windows,there is still gap with C++ and Go,although the latter has no reflection.Hope everything better.

    • Richard LanderMicrosoft employee 0

      Yup. Those are scenarios for us to start tackling in .NET 5 and later. Job security!

      • James Wil 0

        You can do it! we believe in you!
        C# is by far a better language, maybe the best in the market, at least for me
        But when i look at other languages, i envy the way they can distribute tiny exe, saves lot of time when uploading to a server, and runs quickly

  • Mahdi Hosseini 0

    hi tnx for your great work.
    Is it possible to do PublishReadyToRun or p:PublishSingleFile in Publish window?
    I mean, we do not use the command line and get inside Visual Studio without editing project file.
    Do you have any plans for the future? Or should we keep all these codes?

    • Richard LanderMicrosoft employee 0

      Excellent question/point. I will ask the team what the plan is there. I agree that this would make sense.

  • Stig Nielsson 0

    Great news. And great that we apparently now get a .Net Core 5.0 in addition to .Net 5.0 🙂

  • Jefferson Motta 0

    Oh boy. Very nice that .NET Framework is dead and will not be possible call from .NET Core. But for a while, about 10years I guess (the time to the world migrate .NF to .NC) I found a way to call .NET Framework from .NET Core, and works fine.
    I guess .NET Core 5 will be called .NET One, is that?

  • Geovanny Fiallo 0

    Hi Richard, this are good news!
    Maybe you know when we can use .NET Core 3 to make production ready applications?
    Regards

    • Richard LanderMicrosoft employee 0

      We were hoping to give a “go live” license with Preview 7.

  • James Rolfe 0

    Any plans to port more members of “Microsoft.VisualBasic.dll” to .NET Core ? The portability analysis tool indicates that my WPF projects are still not 100% portable to .NET Core because of the heavy use of “MsgBox”. The .NET Framework version of “MsgBox” function depends on Winform. But you can inline the implementation of “MessageBox.Show” (PInvoke MessageBoxW) to “MsgBox” to resolve the dependency problem.

    • Kathleen DollardMicrosoft employee 0

      We are looking for feedback prior to porting more than is in Preview 6. https://github.com/dotnet/corefx is the best place for feedback. 
      Aspects of WinForms are not yet ported, and we want feedback on how to best implement these features. MsgBox is a good example – it’s rather odd in some ways, and does it make more sense to have custom MsgBox implementations? 

Feedback usabilla icon