0Pricing
C# Academy · 강의

ReadyToRun과 계층형 컴파일

.NET에서 ReadyToRun 사전 컴파일, 계층형 JIT 컴파일, PGO(프로파일 기반 최적화)를 이해합니다.

ReadyToRun과 계층형 컴파일은(는) CoddyKit의 무료 C# Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 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과 네이티브 AOT

R2R은 네이티브 코드와 함께 IL도 제공하며 .NET 런타임도 여전히 필요합니다. 네이티브 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 이상에는 기본적으로 동적 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 런타임은 코드 변경 없이 가비지 컬렉션, 스레드 풀 및 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은 전체 애플리케이션(앱 + 프레임워크)을 하나의 공유 네이티브 이미지로 미리 컴파일하여 가능한 최상의 시작 성능을 제공합니다.

// 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)과 네이티브 AOT의 주요 차이점은 무엇입니까?

복습: ReadyToRun 및 계층적 컴파일

핵심 요점:

  • 계층적 컴파일: .NET의 기본값 — Tier 0(빠른 컴파일) → Tier 1(최적화 + PGO)
  • 동적 PGO: .NET 8 이상에서 기본 제공 — 런타임이 자주 실행되는 경로를 프로파일링하고 Tier 1 코드를 특화합니다
  • ReadyToRun: 게시 시 미리 컴파일되어 시작 속도가 약 30% 빠르고 런타임이 여전히 필요하지만 리플렉션을 지원합니다
  • 네이티브 AOT: IL과 런타임이 없고 시작 속도가 약 36배 빠르며 리플렉션을 사용할 수 없고 바이너리가 가장 작습니다
  • runtimeconfig.json과 환경 변수(가비지 컬렉션, 스레드 풀, JIT 설정)를 통해 조정하십시오
  • 복합 R2R은 앱과 프레임워크를 하나의 네이티브 이미지로 병합하여 최상의 시작 성능을 제공합니다

자주 묻는 질문

“ReadyToRun과 계층형 컴파일” 강의는 무료인가요?

네 — “ReadyToRun과 계층형 컴파일” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 C# Academy 강의 전체를 잠금 해제할 수 있습니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“ReadyToRun과 계층형 컴파일”에서 뭘 배우나요?

.NET에서 ReadyToRun 사전 컴파일, 계층형 JIT 컴파일, PGO(프로파일 기반 최적화)를 이해합니다. 브라우저에서 직접 실행하는 실습 코드로 C# Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

C# Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 C# Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“ReadyToRun과 계층형 컴파일” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 C# Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 C# Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 네이티브 AOT 컴파일
  2. 트리밍과 리플렉션의 제한 사항
  3. ReadyToRun과 계층형 컴파일
  4. BenchmarkDotNet을 사용한 벤치마킹
← C# Academy(으)로 돌아가기