V8: за кулисами (Мартовское издание. Запуск I+TF и декларативный JavaScript)

Перевод статьи Benedikt Meurer V8: Behind the Scenes (March Edition feat. I+TF launch and Declarative JavaScript). Опубликовано с разрешения автора. Замечательная иллюстрация от lerazhelezova.

Я снова опаздываю c мартовским изданием, так что это может превратиться в некую традицию. В прошлом месяце было довольно интересно: мы наконец включили Ignition и TurboFan по умолчанию для Chrome M59. Потребовалось две попытки, но, похоже, теперь всё прижилось. В целом все прошло на удивление хорошо, несмотря на сложность и влияние этого изменения. Как сказал мой site director (прим. пер.: к сожалению не удалось найти адекватный перевод этой должности на русский): «Переход с Crankshaft на TurboFan в Chrome был похож на смену движка болида F1 на скорости 250 км/ч».

https://twitter.com/bmeurer/status/839337377671839744

Обратите внимание, что с этим переключением мы также удалили параметр enable-v8-future из chrome://flags и вместо него добавили выключатель disable-v8-ignition-turbo для новой конфигурации в Chrome M59. У этого выключателя есть три возможные настройки: Default, Disabled и Enabled. Если вы оставите его в Default, есть определенная вероятность, что вы получите старый конвейер компилятора даже на канале Canary или Dev из-за A/B тестирования. Установите для него значение «Отключено», чтобы быть уверенными, что вы получили новый конвейер, или установите его во «Включено», чтобы снова использовать Crankshaft и Full-Codegen.

Включение новой конфигурации по умолчанию выявило несколько проблем, которые не были известны ранее, поэтому я был очень занят, помогая с их решением. Но, насколько я могу судить, сейчас все выглядит очень многообещающе.

В конце прошлого месяца я выступил с презентацией TurboFan: новая архитектура генерации кода для V8 на Мюнхенской встрече Node.js User Group, рассказав немного о вещах, которые изменятся с новым конвейером. Самое главное, что включение нового конвейера значительно уменьшает общую сложность V8 и упрощает его перенос на новые архитектуры, а что еще более важно с точки зрения пользователя, полностью исключает необходимость беспокоиться о так называемых убийцах оптимизации, поскольку TurboFan способен обрабатывать каждую конструкцию языка. Это не означает, что внезапно использование странной языковой конструкции, такой как with, становится невероятно быстрым, но это означает, что присутствие одного из этих убийц оптимизации больше не отключает оптимизирующий компилятор для этой конкретной функции.

Источник: TurboFan: A new code generation architecture for V8, MNUG March ’07 Meetup, @bmeurer.

Вероятно, наиболее важными изменениями являются полностью оптимизируемые: операторы try-catch/try-finally, генераторы и асинхронные функции, и, конечно, теперь вы можете использовать let и const, не рискуя попасть в печально известный “Unsupported phi use of const or let variable”.

Одно интересное и несколько неожиданное продолжение дискуссии, вызванное рекомендациями по оптимизации производительности, которые я назвал декларативным JavaScript в своём докладе:

Мало того, что декларативный код легче читать, он также создает красивый байт-код и, следовательно, он быстрее. Спасибо @bmeurer @munichnug

Прежде всего, кажется, что некоторые люди сбиты с толку терминологией. Похоже, что использование понятий «явный против неявного» вместо «декларативный против неясного» для многих более понятно, поэтому просто представьте эти термины, когда смотрите на слайд выше. Однако главным спорным вопросом было то, имеет ли смысл использовать этот совет или предположить, что движок оптимизирует все это под капотом.

Имея бесконечные ресурсы, V8 мог бы генерировать улучшенный код и для правого слайда (и Crankshaft использовал это в прошлом), где он проверяет переменную в условии на то, что она является undefined или Object, и деоптимизирует во всех других случаях. Это уменьшает число проверок в оптимизированном коде. Однако этот подход имеет свою цену: он может деоптимизировать оптимизированный код, как только вводятся новые значения, а движку необходимо отслеживать, какие значения были видны для obj в базовом случае, что требует времени и памяти.

Но реальность такова: V8 не имеет бесконечных ресурсов. Фактически, когда все больше и больше бюджетных Android устройств заходит в мобильный интернет и все меньше и меньше трафика приходится на мобильные устройства или настольные компьютеры высокого класса, кажется, что в будущем у V8 (и Chrome) будет еще меньше ресурсов. Снижение накладных расходов и менее агрессивная оптимизация помогли за последние два года значительно улучшить опыт работы на мобильных устройствах.

Иногда я слышу, как разработчики говорят, что эти проблемы при запуске не имеют значения для Node.js, и проблемы прогрева V8 не должны влиять на то, что они называют серверной производительностью, но затем я слышу, как они жалуются, что TypeScript, UglifyJS, Webpack, Babel и т.д. работают слишком медленно. Эти библиотеки также запускаются на V8 и также сильно страдают от накладных расходов во время прогрева или слишком агрессивной оптимизации, приводящей к частым деоптимизациям. Я видел много доказательств того, что уменьшение чрезмерной оптимизации помогло значительно уменьшить серверные нагрузки, как, например, измеряется в Node.js бенчмарке AcmeAir.

Производительность улучшилась более чем на 45%, в основном за счет снижения оверхеда в V8. Эти измерения были выполнены на типичных клиентских рабочих нагрузках. Для типичных рабочих нагрузок на стороне сервера результат не так уж сильно отличется.

И еще один важный аспект, который легко забывается: V8 — не единственный JavaScript движок, о котором вам следует беспокоиться. Он, безусловно, очень доминирует из-за его использования в Chrome, Node.js, Android и Opera, но ограничивать себя одним движком и производить оптимизации только для него (или надеяться на то, что некоторые оптимизации появятся в V8) — это, безусловно, не путь к успеху.

Говоря о примере выше, вопросы о том, оптимизирует ли движок ToBoolean или нет, и, в частности, какая обратная связь отслеживается и расходуется относительно входящих значений, являются и должны оставаться неважными. Если вы можете написать свой горячий код таким образом, чтобы его можно было оптимизировать независимо от определенных спекулятивных оптимизаций, вы должны одобрить этот способ. Например, когда у вас есть значение obj, которое может быть undefined или объектом, рассмотрите возможность исключения неопределенности через явное условие:

if (obj !== undefined) {
// …
}

или даже лучше:

if (obj !== void 0) {
// …
}

Это так же поможет вам понять ваш собственный код через два года, так как из этого

if (obj) {
// …
}

не ясно, что всё что вы хотели сделать — избежать undefined для obj.

Это особенно полезно иметь в виду при использовании || и && в JavaScript. Например, я видел разработчиков, использующих || для реализации параметров по умолчанию, например:

function foo(a, b) {
a = a || "value";
b = b || 4;
// …
}

Когда я спросил, является ли пустая строка допустимым значением для a или является ли 0 допустимым значением для b, они были весьма удивлены, заметив, что таким образом они ошибочно отфильтровали допустимые входные данные. Так что не делайте этого! Вместо этого используйте функцию ES2015:

function foo(a = "value", b = 4) {
// …
}

Эта конструкция заменяет только значения undefined значениями по умолчанию, что является нормальным. Есть также интересный аспект производительности при использовании && и || в JavaScript. Я посмотрю, смогу ли я написать серию статей о явном JavaScript с уделением особого внимания аспектам производительности с точки зрения виртуальных машин, которые могут помочь пролить некоторый свет на эту тему.

Мой обычный дисклеймер: я не фронтенд инженер. У меня иные приоритеты, чем у вас (возможно), и у вас могут быть веские причины игнорировать любые советы, которые я здесь даю. Это совершенно нормально. Также будьте осторожны с переоптимизацией!

Предыдущие статьи:

V8: За кулисами (февральское издание. История TurboFan)


Слушайте наш подкаст на SoundCloud, читайте на Medium, контрибьютьте на GitHub, общайтесь в группе Telegram, следите в Twitter и канале Telegram, рекомендуйте в VK и Facebook.

Статья на GitHub