Skip to content

carlfranklin/Native-AOT-Test

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Native AOT in .NET 7

Native AOT (Ahead of Time) is a group of technologies that help you build faster and lighter applications, by generating code at build time rather than at runtime, for .NET desktop client and server scenarios.

Native AOT generates 100% native code at build-time with no dependencies.

Native compiling for .NET has been around since the beginning of .NET.

NGEN is a Native Image Generator for .NET Framework only.

ReadyToRun is a format used by crossgen, a .NET Core tool that does a combination of compiling to native and IL (Just In Time).

Mono AOT is used by Xamarin. iOS doesn't allow JIT compiling, so something had to be done there.

If you have been following Blazor, then you know about AOT for WASM, which uses Crossgen under the hood.

Benefits of AOT

You can build self-contained apps that can be copied from system to system as long as they are the same.

Faster load time is great for container apps, and general purpose apps as well.

Smaller memory footprint because only the code that's required is loaded, not the entire CLR. Even though executable size is higher, if you look at memory being used it's much less.

Performance may or may not be sufficiently improved. It will be better, but how much depends on what you're doing.

Drawbacks of AOT

No Loading assemblies using Reflection or using Reflection.Emit for code generation.

May require tweaking for supporting dependencies.

Longer build times.

Prerequisites

In order to take advantage of Native AOT the following prerequisites need to be installed.

Windows

.NET 7

Visual Studio 2022 Preview

Enable Desktop development with C++ workload

Desktop development with C++ workload

Demo

The following demo is a Console Application that captures the time to generate any given numbers using the Fibonacci sequence with and without Native AOT enabled.

Create a Console Application called Fibonacci

Create a new project

Configure your new project

Target .NET 7 (Preview)

Replace Program.cs with the following:

using System.Diagnostics;

var numbersInSequence = 45;
var executions = 1;
var stopWatch = new Stopwatch();
var response = string.Empty;

stopWatch.Start();

for (int i = 1; i <= executions; i++)
{
    Fibonacci(0, 1, 1, numbersInSequence);
}

stopWatch.Stop();
Console.WriteLine($"\n\nTotal Time elapsed for {executions} executions: " +
    $"{stopWatch.ElapsedMilliseconds} milliseconds. Press ENTER");
Console.ReadLine();

static void Fibonacci(int firstNumber, int secondNumber, int numbersProcessed, 
    int numbersInSequence)
{
    Console.Write($"{firstNumber}{(numbersProcessed < numbersInSequence ? ", " : string.Empty)}");
    if (numbersProcessed < numbersInSequence)
    {
        Fibonacci(secondNumber, firstNumber + secondNumber, 
            numbersProcessed + 1, numbersInSequence);
    }
}

Fibonacci numbers are defined at WikiPedia:

In mathematics, the Fibonacci numbers, commonly denoted Fn, form a sequence, the Fibonacci sequence, in which each number is the sum of the two preceding ones.

The Fibonacci function fulfills this. You pass it the first number and the second number, the numbers processed, and the numbers in the sequence, and it calls itself after calculating the next number in the sequence.

I've put the following line in the function to print out the number:

Console.Write($"{firstNumber}{(numbersProcessed < numbersInSequence ? ", " : string.Empty)}");

The StopWatch object measures the time it takes, which is displayed after the sequence is generated and runs.

Run the app and you should see something like this:

image-20220504003945954

Outputting the series into the console adds an overhead because screen drawing. Let's comment out the following line in the Fibonacci method:

Console.Write($"{firstNumber}{(numbersProcessed < numbersInSequence ? ", " : string.Empty)}");

Running the app again shows that it took almost no time:

image-20220504004159296

So, let's bump up the numbersInSequence and executions variables to 10,000:

var numbersInSequence = 10000;
var executions = 10000;

and run it again:

image-20220504004510850

Now we have something we can compare.

Create a new Console Application for testing Native AOT called FibonacciAOT and copy the code from the first application's Program.cs to the new application

Enable Native AOT

In order to enable Native AOT we need to add a reference to the ILCompiler NuGet package, in order to use the Native AOT Compiler and runtime. Since is still in Preview, we first need to add a new NuGet Package source.

Add NuGet Package Source

.NET 7 NuGet Package Source

https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json

.NET 7 NuGet Package Source

Add a reference to the ILCompiler NuGet package.

Tip

In the Manage NuGet Packages screen, make sure All or .NET 7 Preview under Package Source is selected, as well as the Include Prerelease checkbox is checked.

.NET 7 Preview

ILCompiler NuGet package

Create a Publishing Profile

Create a Publishing Profile

Target

Specific Target

Location

Creation Progress

Publishing Profile

Click on Show all settings and change the Target runtime to your desired runtime, in this case I changed it to win-x64

Show all settings

Tip

If you expand the File publish options, ReadyToRun can be set there. We're not going to set this, but I just wanted to show you where it is.

ReadyToRun

Click Save to close Profile settings and Publish to publish the application.

Save and Publish

Important

When building with Native AOT You want to see the ILCompiler is being used,

ILCompiler

as opposed of the Roslyn compiler.

Roslyn

Once published, run the FibonacciAOT.exe file from the output folder.

Results

The results obviously vary depending on multiple factors, such as Memory utilization, Garbage Collection, etc. but overall the results were consistent.

Execution Time (both compiled in Release mode)
  • Fibonacci.exe

    • 243 milliseconds.
  • FibonacciAOT.exe

    • 140 milliseconds.
Memory Utilization

Memory Utilization

Disk Utilization
  • Without Native AOT

    Disk Utilization Without Native AOT

  • With Native AOT

    Disk Utilization With Native AOT

Other Findings

After doing some digging around, I found some useful information.

Important

The reason why the Desktop development with C++ workload is a prerequisite, is because of the Link process,

C++ workload

which come with the C++ Build Tools

C++ Build Tools

Important

The location of the Native files used for building the application can be found in the obj folder.

Native files in obj

Important

The location of the Native files once built can be found in the bin folder.

Native files in bin

Note

Building the application in Linux or macOS would generate Native files for each platform.

Resources

Resource Title Url
Download .NET 7.0 https://dotnet.microsoft.com/en-us/download/dotnet/7.0
Visual Studio 2022 Preview https://visualstudio.microsoft.com/vs/community/
Compiling with Native AOT https://github.com/dotnet/runtime/blob/main/src/coreclr/nativeaot/docs/compiling.md
Native AOT Pre-requisites https://github.com/dotnet/runtime/blob/main/src/coreclr/nativeaot/docs/prerequisites.md
ReadyToRun https://docs.microsoft.com/en-us/dotnet/core/deploying/ready-to-run
Mono AOT https://www.mono-project.com/docs/advanced/aot/

About

Testing IL vs Native AOT Console Apps

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages