I'm Getting Too Old For This JIT - Native Binaries and AOT

One of the things I love about the golang ecosystem is the tools it produces. Dependency-free binaries that start up fast, “just work”, and run with modest (or at least justified) memory requirements. Dotnet on the other had has touted xcopy deployment since its inception, and it works…but only if the .net framework is already installed. And the start-up time can leave a bit to be desired. .Net core itself hasn’t really changed this, but the CoreRT runtime, an open-source .NET core runtime optimized for ahead-of-time (AOT) compilation does. Recently I’ve been experimenting with Avalonia and Core RT to create cross-platform stand-alone applications without any additional runtime dependencies. More accomplished Avalonia dev Wiesław Šoltés has a video of his Core2D drawing application running as a CoreRT complied stand-alone binary.

So far I’ve been impressed with the results. The cold start time is outstanding. The dev environment/compiler tool-chain setup (on windows - the only place I’ve done it so far) was straightforward, and mainly consists of adding the Microsoft.DotNet.ILCompiler package from the dotnet-core myget feed. Some of the biggest hurdles with AOT compilation are the issues arising from runtime code-generation and generics. For runtime code-generation there is discussion about adding an IL interpreter to Core RT (which is apparently what Mono does for AOT platforms). Core RT uses an optional Runtime Directives configuration file - rd.xml which was designed for .NET Native - the “full framework” (i.e. not .net core) AOT compilation tool-chain. This file specifies whether particular types are available for reflection. This information is used by the compilation process to ensure types instanciated via reflection are included in the native binary. You can see the example for the ASP.NET Core RT WebApi demo application here. Here you can see some generic types from JSON.NET added to the rd.xml used by the WebApi demo to ensure they are included in the AOT-compiled binary.

<Assembly Name="System.Linq.Expressions">
    <Type Name="System.Linq.Expressions.ExpressionCreator`1[[Newtonsoft.Json.Serialization.ObjectConstructor`1[[System.Object,System.Private.CoreLib]],Newtonsoft.Json]]" Dynamic="Required All" />

The fact that you need to include this information might at first seem annoying, but it shows that the Core RT tool-chain isn’t just blindly copying all of the types from all of the assemblies you reference into your compiled binary. Instead Core RT traverses from the entry-points of your application or DLL through to the other methods that are called, resulting in a binary that only contains the types and methods you need to run. If you’re looking to get started the Hello World or WebApi samples are a good place to start.

Further Reading

Matt Warren does an excellent job of discussing the history of Core RT and other AOT implementations for the .NET Framework, more detail on how the compiler works etc. Matt Warren - CoreRT - A .NET Runtime for AOT