Теория языков программирования
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?