.NET 8 - AOT Compilation
Hoje vou apresentar a compilação Ahead of Time (AOT) no .NET 8. |
Um dos recursos previstos para o .NET 8 é a compilação Ahead of Time, e neste artigo, vou apresentar o que temos até o momento nas versões preview do .NET 8 SDK. (alguns recursos poderão ser alterados até o lançamento final do .NET 8)
A compilação Ahead of Time (AOT) é um processo de compilação de código fonte em linguagem de programação para um formato executável nativo antes da execução do programa. Ao contrário da compilação Just-in-Time (JIT), que compila o código durante a execução, a compilação AOT ocorre antes do tempo de execução, geralmente como parte do processo de construção ou implantação do software.
A compilação Ahead of Time (AOT) é um dos principais recursos que estão sendo trabalhados pela equipe ASP.NET da Microsoft para o .NET 8.
Seja qual for a linguagem que você usa na plataforma .NET (C#, F#, VB.NET etc), você usa um compilador para gerar os byte codes da linguagem intermediária (IL). Atualmente executamos esta etapa usando o comando dotnet build no projeto para produzir um executável que contém o IL e toda uma carga de metadados.
A próxima etapa do processo é converter o código da IL para o assembly, o que é feito em um tempo de execução pelo runtime do .NET usando o compilador Just-in-Time (JIT).
Considerando a linguagem C# a compilação de um projeto básico envolveria as seguintes etapas:
Na compilação Ahead of Time (AOT) na plataforma .NET, o código C# é compilado diretamente em código de máquina nativo, sem a etapa de geração de código intermediário CIL/IL. Isso permite melhor desempenho e tempo de inicialização mais rápido do programa, além de otimizações específicas da plataforma de destino.
Quais os motivos para usar a compilação AOT e quais as vantagens e desvantagens ?
Prós e Contras da compilação AOT
Como a compilação AOT gera instruções de código assembly, não há compilação JIT em tempo de execução quando o programa é executado. Isso oferece uma grande vantagem - a redução do tempo de inicialização.
Normalmente, antes do runtime do .NET executar um método, ele executa o compilador JIT no IL do método para gerar o código assembly a ser executado. Quando o aplicativo é inicializado, geralmente há muitas classes e métodos que precisam ser compilados pelo JIT. Isso tudo se soma e geralmente significa que os aplicativos .NET podem demorar um pouco para iniciar.
Em aplicativos de servidor tradicionais que são iniciados apenas uma vez e permanecem em execução por horas ou dias, demorar muito para iniciar realmente não importa. Mas o tempo de inicialização é importante para aplicativos que usam AWS Lambda ou Azure Functions. Esses aplicativos são ativados em resposta a uma solicitação, executados uma vez e encerrados. Para esses aplicativos, que podem cobrar por milissegundos, o tempo de inicialização é crucial.
Este cenário específico é onde a compilação AOT para .NET realmente brilharia. É por isso que é um foco para o .NET 8. Entretanto a compilação AOT não será sempre "melhor" do que usar a compilação JIT. Ela apresenta muitas desvantagens, o que significa que o AOT geralmente não é sempre a escolha certa; a abordagem JIT pode ser melhor para o seu caso de uso.
Principais vantagens da compilação AOT:
Principais desvantagens da compilação AOT:
Então em quais cenários seria recomendado usar a compilação Ahead of Time (AOT) ?
Explorando a compilação AOT no .NET 8
No Visual Studio 2022 Preview no template de projeto Console App temos a opção para habilitar a compilação AOT :
Usando a ferramenta NET CLI no prompt de comandos podemos usar o template api : dotnet new api - para criar uma ASP.NET Core Web API que vai gerar uma minimal API onde agora podemos usar a opção --aot.
A opção -aot ajusta o código gerado da seguinte forma:
Então vamos ver isso funcionando na prática...
Estou usando o .NET 8 SDK 8.0.1000-preview.5.23303.2 e vou criar uma api chamada demoaot emitindo o seguinte comando : dotnet new api -o demoaot --aot
Na versão final do .NET 8 o template usado para
gerar uma minimal API com compilação AOT foi alterado para: dotnet new webapiaot |
Entrando na pasta demoaot temos os seguintes arquivos gerados :
Vamos visualizar o código do arquivo Program.cs usando o VS Code :
using
System.Text.Json.Serialization; using demoaot; var builder = WebApplication.CreateSlimBuilder(args); builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); }); var app = builder.Build(); var sampleTodos = TodoGenerator.GenerateTodos().ToArray(); var todosApi = app.MapGroup("/todos"); todosApi.MapGet("/", () => sampleTodos); todosApi.MapGet("/{id}", (int id) => sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo ? Results.Ok(todo) : Results.NotFound()); app.Run(); [JsonSerializable(typeof(Todo[]))] internal partial class AppJsonSerializerContext : JsonSerializerContext { } |
Destaques do código gerado:
1- Note o uso do Slim builder (um novo recurso do .NET 8)
var builder = WebApplication.CreateSlimBuilder(args); |
2- A configuração do JSON source generator
builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); });
|
3- A geração de um array de objetos 'Todo' :
var sampleTodos = TodoGenerator.GenerateTodos().ToArray();
|
4- O contexto de serialização requerido para o source generation :
[JsonSerializable(typeof(Todo[]))] internal partial class AppJsonSerializerContext : JsonSerializerContext { } |
Vejamos agora o código do arquivo demoaot.csproj :
<Project
Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <ServerGarbageCollection>false</ServerGarbageCollection> <InvariantGlobalization>true</InvariantGlobalization> <PublishAot>true</PublishAot> </PropertyGroup> </Project> |
Destaques do código gerado:
1- Desabilita o Garbage Collector para reduzir o consumo de memória
<ServerGarbageCollection>false</ServerGarbageCollection> |
2- Uso da globalização invariant para reduzir o tamanho da aplicação
<InvariantGlobalization>true</InvariantGlobalization> |
3- Habilita a publicação como AOT
<PublishAot>true</PublishAot> |
Publicando a aplicação
Para publicar a aplicação usando a compilação AOT podemos executar o comando: dotnet publish
Observe que, para usar a compilação AOT, você precisa instalar a carga de trabalho "Desenvolvimento de desktop com C++" do Visual Studio 2022 (ou os pré-requisitos descritos aqui).
Sem fazer isso , você pode ver erros como: Linker de plataforma não encontrado ou erro fatal LNK1181: não é possível abrir o arquivo de entrada 'advapi32.lib'.
Após isso podemos entrar na pasta: D:\projetos\demoaot\bin\Release\net8.0\win-x64 e podemos executar o projeto : demoaot.exe
Agora vamos usar o Postman e acessar o endpoint da api em : http://localhost:5000/todos
Nossa aplicação web api compilada com a opção --aot esta respondendo e funcionando e o tamanho do arquivo demoaot.exe gerado é de apenas 157696 bytes.
E estamos conversados.
"Porque pela graça sois salvos, por meio da fé; e isto não vem de vós, é dom
de Deus.
Não vem das obras, para que ninguém se glorie;"
Efésios 2:8,9
Referências: