0Pricing
C# Academy · 课时

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 throughput

Crossgen2 与组合映像

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 原生 AOT 编译
  2. 裁剪与反射限制
  3. ReadyToRun 与分层编译
  4. 使用 BenchmarkDotNet 进行基准测试
← 返回 C# Academy