.NET 6

Rainer Stropek | @rstropek

.NET 6 Status

  • LTS version
    • .NET 5 was not
  • From now on πŸ”—
    • Every November a major version
    • Every second major version will have LTS

Introduction

Rainer Stropek

  • Passionate software developers for 25+ years
    Β 
  • Microsoft MVP, Regional Director
    Β 
  • Trainer, Teacher, Mentor
    Β 
  • πŸ’• community

One
.NET

A little bit
of history...

A little bit of history...

  • .NET Framework at its beginnings
    • Windows only
    • Closed source
  • Mono appeared
    • Separate implementation
    • Open-source, cross platform
  • .NET Core
    • Lean, modern .NET
    • Open-source, cross platform
    • Grew bigger over time
  • Back to one .NET

.NET Standard

.NET Unification Vision

.NET 6

  • Plattform support πŸ”—
    • Nearly the same as .NET 5
    • New: macOS Arm64
    • No big migration hurdles to be expected
  • LTS
    • Patches for three years
  • No new features/APIs to .NET Framework and .NET Core
    • No need to run away
    • Have a plan for switching to the "new world"

Modular SDK

  • .NET Core: Monolithik SDK
  • .NET >= 5: Modular SDK
  • .NET 6: dotnet workloadΒ extended
# list workloads available to install
dotnet workload search

# installs a workload
dotnet workload install

# lists installed workloads
dotnet workload list

# re-install all workloads you’ve previously installed
dotnet workload repair

# update workload
dotnet workload update

# remove the specified workload if you no longer it
dotnet workload uninstall

Crossgen2

aka ReadyToRun format

ReadyToRun File Format

  • Goal: Binary file format for better startup performance
    • Form of ahead of time compilation (AOT)
    • Not full native AOT
    • "Run anywhere" code ranging from full JIT to full AOT (flexible)
    • Re-JITted after start for better steady-state performance
  • Drawbacks
    • Larger file sizes
    • Specific for target architecture (e.g. Win x64)
  • Publish as ReadyToRun πŸ”—
    • In .NET 6: Crossgen2 replaces Crossgen
    • CLI (dotnet publish ... -p:PublishReadyToRun=true)
    • .csproj (<PublishReadyToRun>true</PublishReadyToRun>)

Demo
Time!

Crossgen2 Demo

Measure startup time of ASP.NET Core Web API with EF Core (in-memory provider) with and without ReadyToRun.


Much better startup time, more than double the file size

Without ReadyToRun:

With ReadyToRun:

Sync-over-async Improvements

Sync-over-async

  • Pattern: Use async only in library, sync in API surface
    • Frequently found in legacy apps (e.g. modern lib, rather old UI)
// Note: Sync API, no async/await, no Task
static int DoSomethingSlow()
{
    // Note async in the background
    var result = Task.Run(async () =>
    {
        // Simulate work that takes 2 seconds (e.g. DB or net access)
        await Task.Delay(TimeSpan.FromSeconds(2));
        return 42;
    }).Result; // Note the blocking access to the Result property here

	return result;
}

Sync-over-async Improvements

  • Thread injection happens faster
    • If thread pool is busy just waiting for sync-over-async
  • Consequence: Better performance
  • Drawbacks
    • Larger thread pool
    • Uses more memory
  • So?
    • Prefer modern async/await/Task programming model
    • Less problems when modernizing legacy apps

Demo
Time!

JSON Serialization
Code Generator

What's a Code Generator?

  • Runs during compilation
  • Inspects your program (using Roslyn)
  • Produces additional files that are compiled together with your app

Why Code Generators?

  • Currently:
    • Runtime reflection, e.g. ASP.NET Core DI (impacts performance)
    • Change IL after compilation ("IL weaving")
    • MSBuild tasks
  • In the future:
    • Analyse code at compile time and generate code
    • Less reflection leads to...
      • ...better performance
      • ...smaller apps because AoT compiler (linker) can remove unused parts of your code

Demo
Time!

|                       Method |     Mean |     Error |   StdDev |
|----------------------------- |---------:|----------:|---------:|
|          DeserializeJsonBlob | 3.875 ms | 0.8436 ms | 2.487 ms |
| DeserializeJsonBlobGenerated | 3.748 ms | 0.6969 ms | 2.055 ms |
|                       Method |     Mean |     Error |    StdDev |
|----------------------------- |---------:|----------:|----------:|
|          DeserializeJsonBlob | 2.694 ms | 0.0280 ms | 0.0185 ms |
| DeserializeJsonBlobGenerated | 2.773 ms | 0.0193 ms | 0.0128 ms |

Cold Start Run Strategy

Execution Without Cold Starts

~3.3% improvement

No improvement

Source Generators Behind the Scenes

  • Generator
    • Goal: Learn how a source generator works
      • Build it
      • Running it in compiler and VS2022
    • Offer Generate attribute for partial methods
    • Generates a configurable amount of dummy code
      • No meaning, just generating lots of meaningless C#
      • Kind of c-sharp-ipsum πŸ˜‰
  • Generate code in a library
    • Makes lib pretty large
  • Generate console app using the library

Source Generators Behind the Scenes

[Generator]
public class DummyGenerator : ISourceGenerator
{
    /// <summary>
    /// Visitor class that finds methods to generate
    /// </summary>
    class SyntaxReceiver : ISyntaxContextReceiver
    {
    	...
    }

    public void Initialize(GeneratorInitializationContext context)
    {
        // Register the attribute source
        context.RegisterForPostInitialization((i) => i.AddSource("GenerateAttribute", attributeText));

        // Register a syntax receiver that will be created for each generation pass
        context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
    }

    public void Execute(GeneratorExecutionContext context)
    {
        ...
        // Create string builder for source generation
        var sourceBuilder = new StringBuilder();

		... // Generate code

        // Inject the created source into the users compilation
        context.AddSource("dummy_generated.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
    }
}

Source Generators Behind the Scenes

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

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <IsTrimmable>true</IsTrimmable>
    </PropertyGroup>

    <ItemGroup>
        <ProjectReference Include="..\DummyCodeGenerator\DummyCodeGenerator.csproj"
                          OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
    </ItemGroup>

</Project>

Trimmed Assemblies

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

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <!-- 
            Note that <TrimMode>link</TrimMode> is default in .NET 6.
            See https://docs.microsoft.com/en-us/dotnet/core/deploying/trimming-options#trimming-granularity
        -->
        
        <!-- Note new support for compressing single-file bundles (expanded in-memory) -->
        <EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
    </PropertyGroup>

    <ItemGroup>
        <ProjectReference Include="..\LinkerLibrary\LinkerLibrary.csproj" />
    </ItemGroup>

    <!--
        You can prevent trimming of assemblies. There are many additional options for trimming
        Read more about them at https://docs.microsoft.com/en-us/dotnet/core/deploying/trim-self-contained#prevent-assemblies-from-being-trimmed
    -->
    <ItemGroup>
        <TrimmerRootAssembly Include="System.Text.Json" />
    </ItemGroup>
</Project>

Demo
Time!

JSON Serialization Enhancements

Yet Additional JSON APIs??

  • Support for streaming through IAsyncEnumerableΒ πŸ”—
    • Significant performance improvement in some scenarios
  • JSON Writeable DOM API πŸ”—
    • For scenarios in which POCOs are not an option
      • Data structures not known at runtime
    • Potential for perf optimization
      • Deserialize only a subtree of large JSON in POCOs
      • Manipulate a subtree without deserializing everything into POCOs

Demo
Time!

Linq Enhancements

Community πŸ€˜πŸš€πŸ‘

Demo
Time!

DateOnly πŸ“…
TimeOnly ⏰

finally πŸ₯³πŸ˜…

Demo
Time!

Additional Date/Time-Related Enhancements

  • Better performance of DateTime.UtcNowΒ πŸ”—
  • Enhanced support for time zone names on all platforms πŸ”—

Docker

Demo
Time!

What else?

  • Lots of new analyzers πŸ”—
  • File stream perf improvements on Windows πŸ”—
  • JsonSerializer supports ignoring cycles πŸ”—
  • New PriorityQueue = Queue with priorities πŸ”—
  • More libraries (e.g. SignalR) are annotated for Nullable
  • Enhance startup performance with Profile-Guided Optimization (PGO) πŸ”—

.NET 6 🀘

Rainer Stropek | @rstropek

.NET 6 Intro

By Rainer Stropek

.NET 6 Intro

  • 1,123