Ландшафт движков: V8, JSC, SpiderMonkey
Четыре движка, которые важны — V8, SpiderMonkey, JavaScriptCore, Hermes — разделяют одну адаптивную форму: интерпретатор плюс ярусный JIT плюс GC. Почему трек учит V8, что переносится, и карта терминологии между движками.
Трюк с hidden class, который ускоряет чтение свойств ваших объектов в Chrome, работает и в Firefox, и в Safari — но в Firefox он зовётся «Shape», а в Safari — «Structure». Четыре боевых движка независимо пришли почти к одной и той же архитектуре. Изучите один глубоко — и сможете читать их все; имена меняются, механизм нет.
К концу этого урока вы будете знать, какой движок исполняет ваш код, как выглядит лестница ярусов в каждом из четырёх движков и как переводить термины, когда статья для одного движка описывает паттерн, нужный вам в другом.
Четыре движка, одна архитектура
Есть четыре движка JavaScript, на которых вы, скорее всего, будете выпускать продукт:
- V8 (Google) — Chrome, Edge, Node.js, Deno, Electron, Cloudflare Workers. Самый распространённый; главный предмет этого трека.
- SpiderMonkey (Mozilla) — Firefox. Старейший движок, написан Бренданом Айхом в 1995-м.
- JavaScriptCore / Nitro (Apple) — Safari и движок по умолчанию в Bun. Известен агрессивным многоярусным JIT.
- Hermes (Meta) — React Native. Необычный: компилирует в байт-код заранее и поставляет этот байт-код, оптимизируя запуск приложения на мобильных устройствах в ущерб пиковой пропускной способности.
Несмотря на независимые команды и кодовые базы, первые три сошлись к одной форме: быстрый интерпретатор для холодного кода, один или несколько оптимизирующих JIT-компиляторов для горячего кода, управляемых обратной связью о типах, hidden classes (скрытые классы — внутренние дескрипторы раскладки свойств объекта) для быстрого доступа к свойствам, inline caches (встроенные кеши вызовов — заглушки, запоминающие тип на конкретном сайте) и поколенческий сборщик мусора (generational GC). Эта схожесть и есть причина, по которой трек стоит вашего времени: это не мелочи про V8, это то, как делают быстрыми динамические языки.
Лестница ярусов
Каждый движок прогоняет код через ярусы, обменивая стоимость компиляции на скорость исполнения. Больше вызовов — выше ярус:
Лестница V8 — это Ignition → Sparkplug → Maglev → TurboFan. JavaScriptCore знаменит четырьмя ярусами (LLInt → Baseline → DFG → FTL). SpiderMonkey идёт Interpreter → Baseline → WarpMonkey. Формы рифмуются: дёшево-и-обобщённо внизу, дорого-и-специализированно наверху, а движок повышает и понижает функции по мере того, как меняется их поведение.
Короткая история (чтобы старые посты обретали смысл)
Сам V8 прошёл через поколения, и старые посты предполагают старый дизайн:
- 2008–2010: full-codegen + Crankshaft
- первый JIT
- 2017: Ignition + TurboFan
- эра байт-кода
- 2021: Sparkplug
- базовый JIT
- 2023: Maglev
- средний JIT
- Объект раскладки свойств (термин V8)
- Map
- Та же идея в JSC / SpiderMonkey
- Structure / Shape
Когда вы читаете статью 2015 года про «Crankshaft» и «hidden classes», описанный там оптимизатор уже исчез, но концепция hidden class, которую она учит, по-прежнему ровно верна. Поэтому мы опираемся на механизмы, а не на названия продуктов.
Карта терминологии
Самый большой источник путаницы при чтении про разные движки — именование. Одна идея, три слова:
- Hidden class (дескриптор раскладки свойств объекта):
Mapв V8,Shapeв SpiderMonkey,Structureв JavaScriptCore. - Маленькое целое:
Smiв V8; в других тегируется похоже. - Деоптимизация (откат из скомпилированного кода обратно в интерпретатор):
deoptв V8,bailoutв JSC/SpiderMonkey.
Этот трек повсюду использует словарь V8. Мы будем отмечать межмоторный синоним при первом появлении каждого.
V8, SpiderMonkey и JavaScriptCore сделаны отдельными командами. Почему они пришли к столь похожим архитектурам?
Hermes поставляет предкомпилированный байт-код вместо JIT-компиляции во время работы. Что он оптимизирует?
Расставьте ярусы исполнения V8 от самого дешёвого в производстве (запускается первым) до наиболее оптимизированного (только для самого горячего кода).
- 1 Ignition (интерпретация байт-кода)
- 2 Sparkplug (базовый JIT)
- 3 Maglev (средний JIT)
- 4 TurboFan (оптимизирующий JIT)
▸Граничные случаи
Bun использует JavaScriptCore, а не V8. Это значит, что микробенчмарк, настроенный нативными флагами V8 (--allow-natives-syntax, %OptimizeFunctionOnNextCall), там просто не запустится, а паттерн деопта, кусающий в V8, может вести себя иначе под ярусами DFG/FTL у JSC. Переносимый урок переживёт смену движка; специфичный для движка инструментарий — нет. Всегда отмечайте, на каком движке получены ваши числа.
- 01Назовите четыре важных движка и по одному хосту для каждого.
- 02Во что переводится «hidden class» в SpiderMonkey и JavaScriptCore?
- 03Почему трек опирается на механизмы, а не на названия продуктов вроде «Crankshaft» или «TurboFan»?
Четыре движка несут боевой JavaScript: V8 (Chrome, Node, Deno, Edge), SpiderMonkey (Firefox), JavaScriptCore (Safari, Bun) и Hermes (React Native). Первые три независимо сошлись к одной адаптивной архитектуре — дешёвый интерпретатор, один или несколько оптимизирующих ярусов JIT по обратной связи о типах, hidden classes для раскладки свойств, inline caches и поколенческий сборщик мусора. Лестница ярусов V8 — это Ignition → Sparkplug → Maglev → TurboFan; у JSC и SpiderMonkey свои аналоги. Словарь расходится (Map против Shape против Structure; deopt против bailout), поэтому мы стандартизируемся на терминах V8 и отмечаем синонимы. Мы учим V8, потому что он самый распространённый и лучше всех задокументирован, но ментальная модель переносима. Теперь, когда в статье встретится слово «Shape» или «bailout», вы сразу поймёте, на какой концепт V8 это маппируется — и урок останется в силе вне зависимости от того, на каком движке работает ваш production.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.