ReadyToRun et compilation par niveaux
Comprenez la précompilation ReadyToRun, la compilation JIT par niveaux et PGO (optimisation guidée par le profil) dans .NET.
ReadyToRun et compilation par niveaux est une leçon C# Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C# Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C# Academy comprend 4 leçons au total.
JIT, R2R et compilation par niveaux
.NET utilise plusieurs stratégies de compilation pour équilibrer la rapidité du démarrage et le débit maximal. Comprendre JIT, ReadyToRun et la compilation par niveaux vous aide à choisir les paramètres adaptés à votre charge de travail.
Fonctionnement de JIT
Le compilateur juste-à-temps (JIT) convertit le bytecode IL en code machine natif lors du premier appel de chaque méthode. Cela ajoute de la latence au premier appel, mais produit du code fortement optimisé pour la machine en cours d'exécution.
// 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)Compilation ReadyToRun (R2R)
ReadyToRun (R2R) précompile vos assemblys en code natif au moment de la publication. L'application démarre plus rapidement, car le travail de JIT est déjà effectué, au prix d'une taille sur disque plus importante.
// 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 ou Native AOT
R2R fournit toujours du code IL avec le code natif et nécessite toujours l'environnement d'exécution .NET. Native AOT supprime entièrement l'IL et l'environnement d'exécution. Faites votre choix en fonction de vos exigences de déploiement.
// 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 startupCompilation par niveaux
La compilation par niveaux est activée par défaut dans .NET. Les méthodes commencent au niveau 0 (compilation rapide), puis les méthodes fréquemment utilisées sont recompilées au niveau 1 (optimisation complète) en arrière-plan.
// 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=0Optimisation guidée par le profil (PGO)
.NET 8 et les versions ultérieures incluent la PGO dynamique par défaut. L'environnement d'exécution recueille les données de profil des appels au niveau 0, puis utilise ce profil pour générer du code de niveau 1 hautement spécialisé pour les chemins fréquemment utilisés.
// 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+)Points d'extension du démarrage
Les points d'extension du démarrage (DOTNET_STARTUP_HOOKS) vous permettent d'injecter du code avant l'exécution de Main, ce qui est utile pour les diagnostics, les remplacements de configuration ou l'instrumentation sans modifier le code de l'application.
// 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]Options de configuration de l'environnement d'exécution
L'environnement d'exécution .NET expose des variables d'environnement et des options runtimeconfig.json pour régler le comportement du GC, du pool de threads et de JIT sans modifier le code.
// 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 throughputCrossgen2 et images composites
Crossgen2 est l'outil qui produit les images R2R. Le mode R2R composite précompile une application entière (application et framework) en une seule image native partagée afin d'obtenir le meilleur démarrage possible.
// 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 R2RDans la pratique : régler le démarrage d'ASP.NET Core
Une liste de contrôle des paramètres pratiques permettant de maximiser les performances de démarrage d'un service ASP.NET Core en production.
// .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");Vérification rapide
Quelle est la principale différence entre ReadyToRun (R2R) et Native AOT ?
Récapitulatif : ReadyToRun et compilation par niveaux
Points essentiels :
- Compilation par niveaux : valeur par défaut dans .NET — niveau 0 (compilation rapide) → niveau 1 (optimisé et avec PGO)
- PGO dynamique : valeur par défaut à partir de .NET 8 — l'environnement d'exécution profile les chemins fréquemment utilisés et spécialise le code de niveau 1
- ReadyToRun : précompile lors de la publication ; démarrage environ 30 % plus rapide ; nécessite toujours l'environnement d'exécution ; prend en charge la réflexion
- Native AOT : aucun IL ni environnement d'exécution ; démarrage environ 36 fois plus rapide ; aucune réflexion ; binaire le plus petit
- Réglez ces paramètres via
runtimeconfig.jsonet les variables d'environnement (GC, pool de threads et options de JIT) - Le mode R2R composite fusionne l'application et le framework en une seule image native pour obtenir le meilleur démarrage
Questions Fréquemment Posées
La leçon « ReadyToRun et compilation par niveaux » est-elle gratuite ?
Oui — le texte complet de « ReadyToRun et compilation par niveaux » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C# Academy, passe à CoddyKit PRO. Le cours C# Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « ReadyToRun et compilation par niveaux » ?
Comprenez la précompilation ReadyToRun, la compilation JIT par niveaux et PGO (optimisation guidée par le profil) dans .NET. Tu pratiques C# Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer C# Academy ?
Aucune expérience préalable n'est requise. C# Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.
Combien de temps prend la leçon « ReadyToRun et compilation par niveaux » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon C# Academy ?
Oui. Chaque leçon C# Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Compilation Native AOT
- Limites de la réduction et de la réflexion
- ReadyToRun et compilation par niveaux
- Évaluation des performances avec BenchmarkDotNet