0Pricing
C# Academy · レッスン

ReadyToRunと階層型コンパイル

.NETにおけるReadyToRunの事前コンパイル、階層型JITコンパイル、PGO(プロファイル誘導最適化)を理解します。

「ReadyToRunと階層型コンパイル」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。

JIT、R2R、階層型コンパイル

.NETは、起動速度とピークスループットのバランスを取るために、複数のコンパイル戦略を使用します。JIT、ReadyToRun、階層型コンパイルを理解すると、ワークロードに適した設定を選択できるようになります。

JITの動作

Just-in-Time(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では、階層型コンパイルがデフォルトで有効になっています。メソッドはTier 0(高速コンパイル)から開始し、頻繁に実行されるメソッドはバックグラウンドでTier 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以降では、Dynamic PGOがデフォルトで含まれています。ランタイムはTier 0で呼び出しプロファイルデータを収集し、そのプロファイルを使用してホットパス向けに高度に特化されたTier 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ランタイムは、コードを変更せずにGC、スレッドプール、JITの動作を調整するための環境変数とruntimeconfig.jsonの設定項目を提供します。

// 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はアプリケーション全体(アプリとフレームワーク)を1つの共有ネイティブイメージに事前コンパイルし、可能な限り最高の起動パフォーマンスを実現します。

// 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のデフォルト機能。Tier 0(高速コンパイル)からTier 1(最適化とPGO)へ移行します
  • Dynamic PGO:.NET 8以降のデフォルト機能。ランタイムがホットパスをプロファイルし、Tier 1コードを特化させます
  • ReadyToRun:発行時に事前コンパイルします。起動が約30%高速になり、リフレクションをサポートしますが、ランタイムは必要です
  • Native AOT:ILもランタイムもなく、起動は約36倍高速です。リフレクションは使用できず、バイナリが最小になります
  • runtimeconfig.jsonと環境変数(GC、スレッドプール、JITの設定項目)で調整します
  • 複合R2Rはアプリとフレームワークを1つのネイティブイメージに統合し、最高の起動パフォーマンスを実現します

よくある質問

「ReadyToRunと階層型コンパイル」レッスンは無料ですか?

はい。「ReadyToRunと階層型コンパイル」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。

「ReadyToRunと階層型コンパイル」で何を学びますか?

.NETにおけるReadyToRunの事前コンパイル、階層型JITコンパイル、PGO(プロファイル誘導最適化)を理解します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C# Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「ReadyToRunと階層型コンパイル」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC# Academyレッスンでコードを書いて実行できますか?

はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Native AOTコンパイル
  2. トリミングとリフレクションの制限
  3. ReadyToRunと階層型コンパイル
  4. BenchmarkDotNetによるベンチマーク
← C# Academyに戻る