ReadyToRun 与分层编译
了解 ReadyToRun 预编译、分层 JIT 编译,以及 .NET 中的 PGO(基于性能分析的优化)。
ReadyToRun 与分层编译 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。
JIT、R2R 与分层编译
.NET 使用多种编译策略,在启动速度与峰值吞吐量之间取得平衡。了解 JIT、ReadyToRun 和分层编译有助于您为工作负载选择合适的设置。
JIT 的工作原理
即时编译(JIT)编译器会在每个方法首次调用时,将 IL 字节码转换为本机机器代码。这会增加首次调用的延迟,但能针对正在运行的机器生成高度优化的代码。
// 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(R2R)编译
ReadyToRun(R2R)会在发布时将程序集预编译为本机代码。应用启动更快,因为 JIT 工作已经完成,但代价是磁盘占用空间更大。
// 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 与 Native AOT
R2R 仍会随本机代码一起发布 IL,并且仍然需要 .NET 运行时。Native AOT 会完全移除 IL 和运行时。请根据部署要求进行选择。
// 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分层编译
.NET 默认启用分层编译。方法首先处于第 0 层(快速编译),热点方法则会在后台以第 1 层(完全优化)重新编译。
// 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基于配置文件的优化(PGO)
.NET 8 及更高版本默认包含动态 PGO。运行时会在第 0 层收集调用分析数据,然后使用这些数据为热点路径生成高度专用化的第 1 层代码。
// 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+)启动钩子
启动钩子(DOTNET_STARTUP_HOOKS)允许您在 Main 运行前注入代码,适用于诊断、配置覆盖或检测,而无需修改应用代码。
// 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]运行时配置开关
.NET 运行时提供环境变量和 runtimeconfig.json 配置开关,您无需修改代码即可调整 GC、线程池和 JIT 的行为。
// 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 与组合映像
Crossgen2 是生成 R2R 映像的工具。组合式 R2R 会将整个应用(应用 + 框架)预编译为单个共享本机映像,以实现尽可能快的启动。
// 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实战:调整 ASP.NET Core 启动性能
介绍一份实用设置清单,帮助您最大限度地提升生产环境 ASP.NET Core 服务的启动性能。
// .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");快速检查
ReadyToRun(R2R)与 Native AOT 之间的关键区别是什么?
回顾:ReadyToRun 与分层编译
要点:
- 分层编译:.NET 中的默认设置——第 0 层(快速编译)→ 第 1 层(优化 + PGO)
- 动态 PGO:.NET 8 及更高版本的默认设置——运行时分析热点路径,并专门生成第 1 层代码
- ReadyToRun:在发布时预编译;启动速度提高约 30%;仍需要运行时;支持反射
- Native AOT:没有 IL,也没有运行时;启动速度提高约 36 倍;不支持反射;二进制文件最小
- 通过
runtimeconfig.json和环境变量调整(GC、线程池和 JIT 开关) - 组合式 R2R 将应用和框架合并到一个本机映像中,以实现最佳启动性能
常见问题解答
「ReadyToRun 与分层编译」课时是免费的吗?
是的 — 「ReadyToRun 与分层编译」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「ReadyToRun 与分层编译」这节课中我会学到什么?
了解 ReadyToRun 预编译、分层 JIT 编译,以及 .NET 中的 PGO(基于性能分析的优化)。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「ReadyToRun 与分层编译」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 原生 AOT 编译
- 裁剪与反射限制
- ReadyToRun 与分层编译
- 使用 BenchmarkDotNet 进行基准测试