Startup и lazy parsing05 / 19

Не всё нужно компилировать сразу

Почему размер исходников и стоимость старта связаны, но не равны.

Рассмотрим игру со встроенным редактором уровней. Во время обычной игры функции редактора могут не вызываться. Их полный разбор и компиляция при запуске увеличили бы время старта.

При ленивом разборе (lazy parsing) V8 откладывает полный разбор тела функции и создание её байткода.

Решение принимает парсер V8 при разборе исходного текста. Если настройки движка разрешают ленивую компиляцию, для обычных объявлений функций она используется по умолчанию.

В некоторых случаях V8 предпочитает компиляцию заранее. Например, начало записи (function () { … })() служит для парсера признаком вероятного немедленного вызова. Поэтому такую функцию он стремится сразу полностью разобрать и скомпилировать. Это эвристика — правило по форме кода, позволяющее предположить, что функция скоро понадобится. Официальное объяснение V8.

Ленивую компиляцию также можно отключить настройками движка.

function startGame() { /* Needed immediately */ }
function openEditor() { /* May never be called */ }
startGame();

Рассмотрим ленивый разбор этих объявлений при первом запуске, без готового кода из кэша:

  1. Первоначальный разбор. Preparser проверяет синтаксис тел функций и собирает необходимые сведения об объявлениях переменных и обращениях к ним. Полное синтаксическое дерево и байткод этих функций пока не создаются.
  2. Вызов startGame(). Выполнение программы доходит до вызова. V8 полностью разбирает тело функции, строит AST и генерирует байткод для интерпретатора. Только после этого начинается выполнение тела startGame.
  3. openEditor() пока не вызывается. Её полный разбор и генерация байткода могут оставаться отложенными. Если выполнение позже дойдёт до её вызова, V8 выполнит те же действия перед выполнением её тела.

Здесь результат компиляции — байткод; создание машинного кода JIT-компиляторами рассматривается отдельно.

Таким образом, у V8 есть два варианта: создать байткод заранее или отложить его создание до вызова функции. Конкретные правила зависят от версии и настроек движка.

Отложенная подготовка функций

Два состояния иллюстрируют возможный lazy-путь функции.

НУЖНО СЕЙЧАС
startGame()Подготовка → выполнение
ВОЗМОЖНО ПОЗЖЕ
openEditor()Preparse → отложенная подготовка

Подготовка функции редактора может быть отложена. Preparse всё же выполняет необходимую работу.

1 / 2
Основной вывод

V8 может отложить полный разбор функции и создание её байткода до первого вызова. При таком вызове байткод создаётся до начала выполнения тела функции.

Проверить по первоисточнику ↗

V8 · Lazy parsing↗Preparser проверяет синтаксис и собирает сведения об областях видимости; при первом вызове отложенная функция полностью разбирается и компилируется в байткод.V8 · Выбор компиляции заранее↗Парсер принимает решение до разбора тела функции. Синтаксические признаки вероятного немедленного вызова могут привести к компиляции заранее.