Friday, September 25, 2020

JsonSrcGen + CoreRT = Pure Magic

JsonSrcGen + CoreRT = Pure Magic

In my previous post I talked about how using a Source Generator allows us to make a reflection free Json generator. This has serious advantages when it comes deployment size and startup time.

These advantages become even bigger when paired with an AOT or Ahead Of Time compiler like CoreRT. CoreRT can produce impressively small binaries but cannot do Reflection.Emit and has only limited support for reflection. But seeing as JsonSrcGen doesn't do any reflection the two are a match made in heaven.

For me this produces a binary that is only 2.1 MB and will startup and serialise a simple Json class in only 5 ms.

In order to see just how good this is here a comparison to what you can do with the officially supported .NET Json and self contained publishing options. Which included the new Trimming and Ready To Run options.





How to use JsonSrcGen with CoreRT

1. Create a new .NET 5 console project

dotnet new console

2. Add the CoreRT Package Source

CoreRT requires a custom package source to be added to your nuget.config. I didn't have one so added one using: 

dotnet new nuget

Then add the dotnet-core Package Source to your nuget config so it looks as follows:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
    <add key="nuget" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
</configuration>

3. Add the CoreRT and JsonSrcGen nuget packages

dotnet add package JsonSrcGen

dotnet add package Microsoft.DotNet.ILCompiler

4. Add the following lines to you .csproj file

<LangVersion>preview</LangVersion>
<IlcDisableReflection>true</IlcDisableReflection>
<IlcInvariantGlobalization>true</IlcInvariantGlobalization>

The first line is required because Source Generators are currently in preview, this will not be required once they are released.
The Second line tells CoreRT that we do not need reflection, this is a significant saving in binary size.
The final line tells CoreRT that we do not need localisation support.

5. Install prerequisites
What you need here depends on your platform, but for me on linux I needed the following:

sudo apt-get install clang zlib1g-dev libkrb5-dev libtinfo5

6. Then build the project to get your output

dotnet publish -r linux-x64 -c Release

The -r linux-x64 tells CoreRT to build for linux 64 bit, if you are on a different platform you will need to change this.

7. If you are on linux use the strip command to get rid of debug information
This step is only required on linux, because the debug information ends up in the binary on linux.

strip bin/Release/net5.0/linux-x64/publish/CoreRTSample


Please checkout the JsonSrcGen project on github: https://github.com/trampster/JsonSrcGen

You can find a JsonSrcGen CoreRT sample project in our samples folder

Sunday, September 20, 2020

Introducing JsonSrcGen

Introducing JsonSrcGen

Over the last month I've been working on a new Json Library for .net that takes advantage of the new c# Source Generator feature that is being previewed in .NET 5. I've called this JsonSrcGen which is short for Json Source Generator.

Why yet another Json library?

Source Generators allow us to generate optimal Json serialization code at compile time. Up until now Json Libraries have made heavy use of Reflection and Reflection.Emit to produce serialization code at runtime. But this approach has some draw backs.
  • Startup times are slow because of the reflection and time to emit the serialization code.
  • AOT (Ahead of Time) platforms like Xamarin.iOS, Blazor and CoreRT struggle with Reflection and cannot do Reflection.Emit. Even if they can do reflection they can get significant size savings if they exclude it
  • They require slow runtime lookups to match Types to generated Serializers

How do I use it?

Source Generators currently require .NET 5 and LangVersion set to 'preview'
<TargetFramework>net5.0</TargetFramework>
<LangVersion>preview</LangVersion>

Add a nuget reference to our nuget package

Annotate your c# class with JsonSrcGen Attributes:

[Json]
public class MyType
{
    public int Age {get;set}

    [JsonName("my_property")]
    public string MyProperty {get;set}   

    [JsonIgnore]
    public string IgnoredProperty {get;set;}
}

Then use JsonSrcGenConvert instance to convert to and from Json strings.

var converter = new JsonSrcGenConvert();

//convert from Json
MyType myType = new MyType();
converter.FromJson(myType, jsonString);

//convert to json
jsonString = converter.ToJson(new MyType());

Notice that when converting from Json you supply an already instantiated instance of the type, this allows you to reuse type instances and thus reduce memory allocations. Which adds up to a significant performance boost while deserializing.

How fast is it?

I am not going to make any claims about the performance of JsonSrcGen. Many .net serializers have made claims about being the fastest only to have their claims age very poorly. Instead I will just say that JsonSrcGen has a strong focus on performance. Below is a benchmark of serialization and deserialization of a simple class. However I strongly advise you to benchmark against your own types and your own use case to determine which is best for you.

How do I get it?

JsonSrcGen is available on Nuget in here

The code is available on github here under the MIT license.

JsonSrcGen is currently in alpha quality and should not be used for production code. However feel free to try it out and report any issues you have with it.