0Pricing
C# Academy · Aula

ReadyToRun e compilação em camadas

Entenda a pré-compilação ReadyToRun, a compilação JIT em camadas e PGO (otimização orientada por perfis) no .NET.

ReadyToRun e compilação em camadas é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.

JIT, R2R e compilação em camadas

O .NET usa várias estratégias de compilação para equilibrar a velocidade de inicialização com o desempenho máximo. Compreender JIT, ReadyToRun e a compilação em camadas ajuda você a escolher as configurações certas para sua carga de trabalho.

Como funciona o JIT

O compilador Just-in-Time (JIT) converte o código de bytes IL em código de máquina nativo na primeira vez que cada método é chamado. Isso acrescenta latência à primeira chamada, mas produz código altamente otimizado para a máquina em execução.

// What happens at runtime:
// 1. App starts — methods are stubs pointing to the JIT
// 2. First call to MyMethod() → JIT compiles IL → native x64 code
// 3. Subsequent calls → native code runs directly (no JIT overhead)

// Startup cost: each method's first call takes microseconds for JIT
// Peak performance: excellent — JIT knows the actual CPU features

// .NET 9 JIT optimizations applied automatically:
// - Inlining
// - Loop unrolling
// - Escape analysis
// - SIMD vectorization
// - Profile-Guided Optimization (PGO)

Compilação ReadyToRun (R2R)

O ReadyToRun (R2R) pré-compila seus assemblies em código nativo no momento da publicação. A aplicação inicia mais rapidamente porque o trabalho do JIT já foi realizado, ao custo de um tamanho maior em disco.

// Enable R2R in .csproj:
<PropertyGroup>
  <PublishReadyToRun>true</PublishReadyToRun>

  <!-- Optional: composite R2R — whole app in one native image -->
  <PublishReadyToRunComposite>true</PublishReadyToRunComposite>
</PropertyGroup>

// Publish:
dotnet publish -c Release -r linux-x64 --self-contained

// Benefits:
// - Startup time improvement: ~20-30% faster than JIT-only
// - No runtime required (self-contained)

// Trade-off:
// - Larger binary (IL + R2R code both stored)
// - Publish takes longer (cross-gen2 runs at publish time)

R2R versus Native AOT

O R2R ainda distribui IL junto com o código nativo e ainda requer o ambiente de execução do .NET. Native AOT remove completamente o IL e o ambiente de execução. Escolha com base nos requisitos da sua implantação.

// ReadyToRun:
// ✓ Still ships IL (fallback JIT for dynamically-generated code)
// ✓ Supports full reflection
// ✓ .NET runtime required on target
// ✓ Startup: ~30% faster than JIT
// Size: medium (IL + native)

// Native AOT:
// ✓ No IL, no runtime needed on target
// ✓ Startup: ~36x faster than JIT
// ✗ No reflection (must use source generators)
// ✗ Trimming limitations
// Size: smallest

// JIT only:
// ✓ Full reflection, no limitations
// ✓ Best peak throughput via PGO
// ✗ Slowest startup

Compilação em camadas

A compilação em camadas é habilitada por padrão no .NET. Os métodos começam na Camada 0 (compilação rápida), e os métodos muito utilizados são recompilados na Camada 1 (otimização completa) em segundo plano.

// Tiered compilation lifecycle:
// Tier 0: fast JIT with minimal optimization
//   → method is callable in ~100 microseconds
// After ~30 calls (configurable): marked as "hot"
// Tier 1: full JIT with all optimizations + PGO
//   → background thread re-compiles the method
//   → future calls use optimized native code

// Usually transparent — you don't need to configure it
// Disable (rarely needed, only for debugging JIT issues):
// DOTNET_TieredCompilation=0

// Disable tiered PGO if benchmarking:
// DOTNET_TieredPGO=0

Otimização guiada por perfil (PGO)

O .NET 8+ inclui PGO dinâmico por padrão. O ambiente de execução coleta dados de perfil de chamadas na Camada 0 e depois usa esse perfil para gerar código altamente especializado da Camada 1 para os caminhos mais utilizados.

// PGO automatically specializes code like:

// Before PGO (generic virtual dispatch):
void Process(IAnimal animal) => animal.Speak();
// → runtime checks type on every call

// After PGO (type-specialized inline):
void Process(IAnimal animal)
{
    if (animal is Dog)     { /* inlined Dog.Speak() */ }
    else if (animal is Cat){ /* inlined Cat.Speak() */ }
    else animal.Speak();   // slow path
}

// Enabled by default in .NET 8+
// Verify:
// DOTNET_ReadyToRun=0
// DOTNET_TieredCompilation=1  (default)
// DOTNET_TieredPGO=1          (default .NET 8+)

Ganchos de inicialização

Os ganchos de inicialização (DOTNET_STARTUP_HOOKS) permitem injetar código antes da execução de Main, o que é útil para diagnósticos, substituições de configuração ou instrumentação sem modificar o código da aplicação.

// Create a hook class:
public class StartupHook
{
    public static void Initialize()
    {
        Console.WriteLine("[StartupHook] Initializing before Main");
        // e.g., register telemetry, inject config overrides
        Environment.SetEnvironmentVariable("FEATURE_FLAG", "1");
    }
}

// Compile to a DLL and set the env var:
export DOTNET_STARTUP_HOOKS=/path/to/MyHook.dll

// Then run your app normally:
dotnet run
// [StartupHook] Initializing before Main
// [Your App Output]

Opções de configuração do ambiente de execução

O ambiente de execução do .NET expõe variáveis de ambiente e opções em runtimeconfig.json para ajustar a coleta de lixo, o pool de threads e o comportamento do JIT sem alterações no código.

// runtimeconfig.template.json (baked into app at publish):
{
  "configProperties": {
    "System.GC.Server": true,           // server GC (multi-core)
    "System.GC.Concurrent": true,       // background GC
    "System.GC.HeapHardLimit": 536870912, // 512 MB hard limit
    "System.Threading.ThreadPool.MinThreads": 20,
    "System.Threading.ThreadPool.MaxThreads": 200,
    "System.Runtime.TieredCompilation": true
  }
}

// Environment variable overrides (useful in containers):
// DOTNET_GCHeapHardLimit=536870912
// DOTNET_ThreadPool_UnfairSemaphoreSpinLimit=70
// DOTNET_GCConserve=1  // reduce memory at cost of throughput

Crossgen2 e imagens compostas

Crossgen2 é a ferramenta que produz imagens R2R. O R2R composto pré-compila uma aplicação inteira (aplicação + estrutura) em uma única imagem nativa compartilhada para obter a melhor inicialização possível.

// Composite R2R: compile app + framework into one native image
<PropertyGroup>
  <PublishReadyToRun>true</PublishReadyToRun>
  <PublishReadyToRunComposite>true</PublishReadyToRunComposite>
</PropertyGroup>

// Normal R2R includes multiple images per assembly
// Composite merges them → fewer file reads at startup

// Manual crossgen2 invocation:
dotnet tool install -g dotnet-crossgen2
crossgen2 \
  --composite \
  --out composite.dll \
  MyApp.dll framework/System.Runtime.dll

// Inspect R2R content:
dotnet-ildasm --methodheader MyApp.dll | grep --color R2R

Caso real: ajustando a inicialização do ASP.NET Core

Uma lista de verificação de configurações práticas para maximizar o desempenho de inicialização de um serviço ASP.NET Core em produção.

// .csproj:
<PublishReadyToRun>true</PublishReadyToRun>  // pre-JIT at publish
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<TieredCompilation>true</TieredCompilation>   // default

// Program.cs — avoid startup work:
// ✓ Use AddHealthChecks() NOT AddDbContextCheck() for faster probe
// ✓ Lazy-initialize expensive singletons
// ✓ Don't await long tasks in Program.cs before app.Run()

// Container settings:
// DOTNET_GCServer=1             → server GC for multi-core pods
// DOTNET_GCHeapHardLimit=536870912 → prevent OOM in constrained pods
// DOTNET_ThreadPool_MinThreads=20  → reduce thread starvation under burst load

// Measure startup:
// app.MapGet("/startup-time",
//   () => $"Ready in: {Stopwatch.GetElapsedTime(startTimestamp).TotalMs}ms");

Verificação rápida

Qual é a principal diferença entre ReadyToRun (R2R) e Native AOT?

Recapitulação: ReadyToRun e compilação em camadas

Principais conclusões:

  • Compilação em camadas: padrão no .NET — Camada 0 (compilação rápida) → Camada 1 (otimizada + PGO)
  • PGO dinâmico: padrão no .NET 8+ — o ambiente de execução cria perfis dos caminhos mais utilizados e especializa o código da Camada 1
  • ReadyToRun: pré-compila no momento da publicação; inicialização aproximadamente 30% mais rápida; ainda precisa do ambiente de execução; oferece suporte à reflexão
  • Native AOT: sem IL e sem ambiente de execução; inicialização aproximadamente 36 vezes mais rápida; sem reflexão; menor binário
  • Ajuste por meio de runtimeconfig.json e variáveis de ambiente (coleta de lixo, pool de threads e opções do JIT)
  • O R2R composto combina a aplicação e a estrutura em uma única imagem nativa para obter a melhor inicialização

Perguntas Frequentes

A aula “ReadyToRun e compilação em camadas” é grátis?

Sim — o texto completo de “ReadyToRun e compilação em camadas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.

O que vou aprender em “ReadyToRun e compilação em camadas”?

Entenda a pré-compilação ReadyToRun, a compilação JIT em camadas e PGO (otimização orientada por perfis) no .NET. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C# Academy?

Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “ReadyToRun e compilação em camadas”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C# Academy?

Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Compilação Native AOT
  2. Limitações de corte e reflexão
  3. ReadyToRun e compilação em camadas
  4. Benchmarking com BenchmarkDotNet
← Voltar para C# Academy