C# Academy · Lektion

ReadyToRun og tiered compilation

Forstå ReadyToRun-forhåndskompilering, tiered JIT-kompilering og PGO (profile-guided optimization) i .NET.

Lektion 3 af 412 trin

ReadyToRun og tiered compilation er en gratis C# Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i C# Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. C# Academy-kurset indeholder 4 lektioner i alt.

JIT, R2R og trinvis kompilering

.NET bruger flere kompileringsstrategier for at afveje opstartshastighed mod maksimal gennemløbshastighed. Hvis du forstår JIT, ReadyToRun og trinvis kompilering, kan du vælge de rigtige indstillinger til din arbejdsbelastning.

Sådan fungerer JIT

Just-in-Time-kompileren (JIT) konverterer IL-bytecode til native maskinkode, første gang hver metode kaldes. Det tilføjer latenstid ved det første kald, men producerer meget optimeret kode til den maskine, der kører programmet.

// 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)

ReadyToRun-kompilering (R2R)

ReadyToRun (R2R) forudkompilerer dine assemblyer til nativekode ved udgivelse. Applikationen starter hurtigere, fordi JIT-arbejdet allerede er udført, men det giver en større størrelse på disken.

// 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 sammenlignet med Native AOT

R2R leveres stadig med IL sammen med nativekoden og kræver stadig .NET-runtime. Native AOT fjerner både IL og runtime fuldstændigt. Vælg ud fra dine krav til implementeringen.

// 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

Trinvis kompilering

Trinvis kompilering er som standard aktiveret i .NET. Metoder starter på niveau 0 (hurtig kompilering), og metoder, der bruges ofte, kompileres igen på niveau 1 (fuld optimering) i baggrunden.

// 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

Profilstyret optimering (PGO)

.NET 8+ inkluderer dynamisk PGO som standard. Runtime indsamler profildata for kald på niveau 0 og bruger derefter disse data til at generere meget specialiseret kode på niveau 1 til ofte benyttede kodeveje.

// 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+)

Opstartshooks

Opstartshooks (DOTNET_STARTUP_HOOKS) giver dig mulighed for at indsætte kode, før Main kører, hvilket er nyttigt til diagnosticering, konfigurationstilsidesættelser eller instrumentering uden at ændre applikationskode.

// 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]

Konfigurationsindstillinger for runtime

.NET-runtime stiller miljøvariabler og indstillinger i runtimeconfig.json til rådighed, så du kan justere GC, trådpuljen og JIT-adfærden uden kodeændringer.

// 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 og sammensatte afbildninger

Crossgen2 er værktøjet, der producerer R2R-afbildninger. Sammensat R2R forudkompilerer en hel applikation (applikation + framework) til én delt nativeafbildning for den bedst mulige opstart.

// 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

Virkeligt eksempel: Justering af ASP.NET Core-opstart

En tjekliste med praktiske indstillinger til at maksimere opstartsydelsen i en ASP.NET Core-tjeneste i produktion.

// .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");

Hurtigt tjek

Hvad er den vigtigste forskel mellem ReadyToRun (R2R) og Native AOT?

Opsummering: ReadyToRun og trinvis kompilering

Vigtigste pointer:

  • Trinvis kompilering: standard i .NET — niveau 0 (hurtig kompilering) → niveau 1 (optimeret + PGO)
  • Dynamisk PGO: standard fra .NET 8 og frem — runtime profilerer ofte benyttede kodeveje og specialiserer kode på niveau 1
  • ReadyToRun: forudkompilerer ved udgivelse; ca. 30 % hurtigere opstart; kræver stadig runtime; understøtter refleksion
  • Native AOT: ingen IL, ingen runtime; ca. 36 gange hurtigere opstart; ingen refleksion; mindste binærfil
  • Justér via runtimeconfig.json og miljøvariabler (GC, trådpulje og JIT-indstillinger)
  • Sammensat R2R samler applikation og framework i én nativeafbildning for den bedste opstart
Gratis at komme i gang

Lær C# med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
93
Lektioner
346

Ofte stillede spørgsmål

Er lektionen “ReadyToRun og tiered compilation” gratis?

Ja — hele teksten til “ReadyToRun og tiered compilation” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af C# Academy-kurset, skal du opgradere til CoddyKit PRO. C# Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “ReadyToRun og tiered compilation”?

Forstå ReadyToRun-forhåndskompilering, tiered JIT-kompilering og PGO (profile-guided optimization) i .NET. Du øver dig i C# Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på C# Academy?

Der kræves ingen tidligere erfaring. C# Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “ReadyToRun og tiered compilation”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne C# Academy-lektion?

Ja. Alle C# Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Native AOT-kompilering
  2. Trimming og begrænsninger ved reflection
  3. ReadyToRun og tiered compilation
  4. Benchmarking med BenchmarkDotNet
← Tilbage til C# Academy