Теория языков программирования

JIT Компиляция

JavaScript который запускается в браузере часто быстрее Python. Это кажется парадоксальным - динамический язык с type coercion быстрее 'просто языка'. Секрет: V8 Turbofan JIT со спекулятивными оптимизациями работает как самонастраивающийся компилятор, специализируясь под конкретные типы и паттерны исполняемого кода.

  • **LuaJIT в OpenResty**: Nginx + LuaJIT обрабатывает 1M req/sec на одном сервере. Cloudflare использует это для edge computing. LuaJIT tracing JIT часто быстрее C для числовых вычислений
  • **V8 в Node.js**: Node 18+ с Maglev JIT (новый baseline compiler). TypeScript компилятор написан на TypeScript - запускается в V8 JIT. Компиляция 100K строк TypeScript за 3 сек
  • **GraalVM**: один JIT для Java, JavaScript, Python, Ruby, R. Polyglot JIT: Python код вызывающий Java библиотеки компилируется вместе без overhead межъязыковых вызовов

Tracing JIT

Tracing JIT записывает конкретный путь выполнения (trace) через горячий цикл и компилирует именно этот путь в нативный код. LuaJIT использует tracing JIT - один из быстрейших динамических языков, часто обгоняет Python в 100x. Trace включает инлайнинг вызовов, специализацию по типам, устранение динамической диспатчи.

LuaJIT часто быстрее C кода для числовых вычислений из-за автоматической векторизации трассы. Nginx использует LuaJIT для обработки HTTP запросов в nginx-module-lua. Cloudflare Workers используют V8 (method JIT) для изоляции. Разные подходы для разных use case.

Что такое 'side exit' в tracing JIT?

Method JIT

Method JIT (HotSpot JVM, V8 Turbofan) компилирует метод целиком, а не trace. Уровни: интерпретатор -> baseline JIT (быстрая компиляция, без оптимизаций) -> optimizing JIT (медленная компиляция, агрессивные оптимизации). HotSpot: C1 (client) + C2 (server) компиляторы.

Почему V8 использует несколько уровней компиляции вместо одного оптимизирующего компилятора?

Деоптимизация

Деоптимизация - откат от оптимизированного кода к интерпретатору когда спекулятивное предположение нарушается. V8: если функция была оптимизирована для Number + Number, первый вызов со строкой вызывает deoptimization. Часто встречающиеся deopt паттерны - источник проблем с производительностью.

Почему `delete object.property` может вызвать деоптимизацию в V8?

Спекулятивные оптимизации

Спекулятивные оптимизации - компиляция кода с предположениями которые могут оказаться ложными. Inline caches (IC) - кешируют тип объекта для которого выполнялся dispatch. Polymorphic IC - несколько типов. Megamorphic IC - слишком много типов, оптимизация отключается.

JIT компиляция всегда делает код быстрее чем интерпретация

JIT имеет warmup время. Функция вызванная один раз работает медленнее на JIT чем в AOT-компилируемом языке. JIT выигрывает только на горячих путях выполнения

Serverless functions - конкретный пример. Каждый cold start = новая JVM/V8 без прогретого JIT. Amazon Lambda с Java 17: первый запрос 500ms, следующие 5ms. GraalVM Native Image или AOT компиляция решает это ценой потери некоторых JIT оптимизаций

Что происходит когда Inline Cache становится megamorphic?

Итоги

  • **Tracing JIT**: записывает горячий путь (LuaJIT). Специализация по конкретному trace. Side exit при нарушении guards
  • **Method JIT**: компилирует метод целиком (V8, HotSpot). Tiered: интерпретатор -> baseline -> optimizing. Feedback от профилировщика
  • **Деоптимизация**: нарушение спекулятивного предположения -> откат к интерпретатору. delete, смена типов, megamorphic IC - триггеры
  • **Inline Cache**: monomorphic (1 тип) -> polymorphic (2-4) -> megamorphic (5+). Megamorphic = generic lookup, нет оптимизации

Связанные темы

JIT объединяет техники компиляторов с runtime профилированием:

  • IR и оптимизации — JIT применяет те же оптимизации что и AOT компилятор - inlining, DCE, constant folding - но на основе runtime данных
  • WebAssembly — WASM создан чтобы обойти JIT нестабильность - предсказуемая производительность без warmup и deoptimization

Вопросы для размышления

  • JIT специализирует код под конкретные типы. Но TypeScript статически типизирован. Почему V8 всё равно нужен JIT а не компиляция в нативный код напрямую?
  • GraalVM polyglot JIT: Python вызывает Java без overhead. Как это возможно если Python и Java имеют совершенно разные object models и memory layouts?
  • Serverless cold start проблема: JVM холодный старт 300-500ms. GraalVM Native Image: 10ms старт но без JIT оптимизаций. Как ChooseRuntime для different workloads?

Связанные уроки

  • arch-06-pipelining
JIT Компиляция

0

1

Войти