0Pricing
C# Academy · Урок

ReadyToRun и многоуровневая компиляция

Разберитесь в предварительной компиляции ReadyToRun, многоуровневой JIT-компиляции и PGO (оптимизации на основе профиля) в .NET.

«ReadyToRun и многоуровневая компиляция» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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 для настройки сборщика мусора, пула потоков и поведения 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 и переменные окружения (параметры сборщика мусора, пула потоков и JIT)
  • Составной R2R объединяет приложение и платформу в один нативный образ для максимально быстрого запуска

Часто задаваемые вопросы

Урок «ReadyToRun и многоуровневая компиляция» бесплатный?

Да — полный текст урока «ReadyToRun и многоуровневая компиляция» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «ReadyToRun и многоуровневая компиляция»?

Разберитесь в предварительной компиляции ReadyToRun, многоуровневой JIT-компиляции и PGO (оптимизации на основе профиля) в .NET. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «ReadyToRun и многоуровневая компиляция»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Компиляция Native AOT
  2. Обрезка и ограничения отражения
  3. ReadyToRun и многоуровневая компиляция
  4. Бенчмаркинг с BenchmarkDotNet
← Назад к C# Academy