Празднование 10-летия V8
В этом месяце отмечается десятилетие не только выпуска Google Chrome, но и проекта V8. В этом посте представлен обзор основных этапов проекта V8 за последние 10 лет, а также за годы до этого, когда проект был ещё секретным.
gource
.До выпуска V8: ранние годы
Google наняла Ларса Бака осенью 2006 года для разработки нового JavaScript-движка для веб-браузера Chrome, который на тот момент оставался секретным внутренним проектом Google. Ларс недавно вернулся в Орхус, Дания, из Силиконовой долины. Так как в Орхусе не было офиса Google, а Ларс хотел остаться в Дании, он и несколько инженеров, работавших над проектом, начали работать в пристройке на его ферме. Новый JavaScript-движок получил название “V8”, что является игривой отсылкой к мощному двигателю, который можно найти в классическом маслкаре. Позже, когда команда V8 выросла, разработчики переехали из скромных помещений на ферме в современное офисное здание в Орхусе, но сохранили свою целеустремлённость и фокус на создании самого быстрого JavaScript-движка в мире.
Запуск и развитие V8
V8 стал открытым исходным проектом в тот же день, когда был запущен Chrome: 2 сентября 2008 года. Первый коммит был сделан 30 июня 2008 года. До этой даты разработка V8 велась в закрытом репозитории CVS. Изначально V8 поддерживал только инструкции ia32 и ARM и использовал SCons в качестве системы сборки.
В 2009 году был представлен абсолютно новый движок для регулярных выражений под названием Irregexp, что привело к улучшению производительности для реальных регулярных выражений. С добавлением порта для x64 количество поддерживаемых наборов инструкций увеличилось с двух до трёх. 2009 год также ознаменовался первым выпуском проекта Node.js, который использует движок V8. Возможность использовать V8 в проектах вне браузера была явно упомянута в оригинальном комиксе Chrome. С Node.js это стало реальностью! Node.js выросла в одну из самых популярных экосистем JavaScript.
В 2010 году производительность движка значительно повысилась благодаря введению нового оптимизирующего JIT-компилятора. Crankshaft генерировал машинный код, который был в два раза быстрее и на 30% меньше, чем предыдущий (безымянный) компилятор V8. В том же году V8 добавил четвёртый набор инструкций: 32-битный MIPS.
В 2011 году значительно улучшилась система сборки мусора. Новый инкрементальный сборщик мусора существенно сократил время паузы, поддерживая отличную производительность и низкое использование памяти. V8 представил концепцию Изолятов, которая позволяет внедрять несколько экземпляров движка V8 в одном процессе, проложив путь для легковесных веб-воркеров в Chrome. Произошла первая миграция системы сборки, когда мы перешли с SCons на GYP. Были реализованы поддержка строгого режима ES5 и другие улучшения. Между тем разработка переехала из Орхуса в Мюнхен (Германия) под новым руководством с активным взаимодействием с оригинальной командой Орхуса.
2012 год стал годом установления ключевых показателей для проекта V8. Команда проводила ускоренные спринты для оптимизации производительности V8, измеряемой с помощью наборов тестов SunSpider и Kraken. Позже мы разработали новый набор тестов под названием Octane (с V8 Bench в его основе), который сделал соревнование по достижению максимальной производительности одной из главных задач и стимулировал значительные улучшения в технологиях выполнения и JIT во всех основных JS-движках. Одним из результатов этих усилий стал переход от случайного выборочного профилирования к детерминированной, основанной на счёте технике для обнаружения “горячих” функций в профайлере времени выполнения V8, что значительно снизило вероятность случайного замедления загрузки страниц (или запуска тестов).
2013 стал годом появления низкоуровневого подмножества JavaScript, названного asm.js. Поскольку asm.js ограничен статически типизированной арифметикой, вызовами функций и доступом к куче только с примитивными типами, проверенный код asm.js мог выполняться с предсказуемой производительностью. Мы выпустили новую версию Octane — Octane 2.0 с обновлениями существующих тестов, а также новыми тестами, нацеленными на такие сценарии использования, как asm.js. Octane стимулировал разработку новых оптимизаций компилятора, таких как свёртка распределений и оптимизации распределений на основе мест переходов типов и преждевременных выделений, которые сильно улучшили пиковую производительность. В рамках инициативы, которую мы внутри команды назвали “Handlepocalypse”, API для работы с хендлами в V8 был полностью переписан, чтобы сделать его более простым и безопасным в использовании. Также в 2013 году реализация TypedArray
в JavaScript в Chrome была перемещена из Blink в V8.
В 2014 году V8 перенес часть работы по JIT-компиляции с основного потока на параллельную компиляцию, что уменьшило задержки и значительно повысило производительность. Позже в этом году мы запустили начальную версию нового оптимизирующего компилятора под названием TurboFan. Тем временем наши партнёры помогли портировать V8 для трёх новых архитектур набора инструкций: PPC, MIPS64 и ARM64. Вслед за Chromium, V8 перешел на ещё одну систему сборки — GN. Тестовая инфраструктура V8 была значительно улучшена: Tryserver теперь позволял тестировать каждый патч на различных сборочных ботах перед его интеграцией. Для контроля версий V8 перешёл с SVN на Git.
2015 был насыщенным годом для V8 по многим направлениям. Мы реализовали кэширование кода и потоковую обработку скриптов, что значительно ускорило загрузку веб-страниц. Работа над использованием мементов распределения в нашей системе времени выполнения была опубликована на ISMM 2015. Позже в этом году мы начали работу над новым интерпретатором под названием Ignition. Мы экспериментировали с идеей подмножества JavaScript, названного strong mode, чтобы достичь более строгих гарантий и предсказуемой производительности. Strong mode был реализован за флагом, но позже мы обнаружили, что его преимущества не оправдывают затраты. Добавление очереди коммитов значительно улучшило производительность и стабильность разработки. Сборщик мусора в V8 также начал сотрудничать с внедренным программным обеспечением, таким как Blink, для планирования работы сборщика мусора в периоды простоя. Сборка мусора в периоды простоя существенно снизила визуальные задержки, связанные с сборкой мусора, и объемы используемой памяти. В декабре первая сборка WebAssembly была интегрирована в V8.
В 2016 году мы завершили работу над последними элементами ES2015 (ранее известного как "ES6") — набора функций (включая промисы, синтаксис классов, лексическое замыкание, деструктуризацию и другие), а также некоторыми функциями ES2016. Мы начали внедрение новой конвейерной системы Ignition и TurboFan, используя её для компиляции и оптимизации функций ES2015 и ES2016, и сделали Ignition стандартным для бюджетных устройств на Android. Успешная работа по сбору мусора в режиме ожидания была представлена на PLDI 2016. Мы запустили проект Orinoco — новый, в основном параллельный и конкурентный сборщик мусора для V8, чтобы сократить время сборки мусора основным потоком. В рамках крупной переориентации мы сместили акцент с синтетических микро-бенчмарков на серьёзное измерение и оптимизацию производительности в реальных условиях. Для отладки инспектор V8 был перемещён из Chromium в V8, что позволило любому пользователю V8 (а не только Chromium) использовать Chrome DevTools для отладки JavaScript, работающего в V8. Прототип WebAssembly перешёл от прототипа к экспериментальной поддержке, в координации с другими поставщиками браузеров экспериментальная поддержка WebAssembly. V8 получил премию ACM SIGPLAN Programming Languages Software Award. И был добавлен ещё один порт: S390.
В 2017 году мы наконец завершили многолетнюю капитальную переработку движка, включив новую конвейерную систему Ignition и TurboFan по умолчанию. Это позволило впоследствии удалить Crankshaft (130,380 удалённых строк кода) и Full-codegen из базы кода. Мы запустили Orinoco v1.0, включая конкурентную маркировку, конкурентную зачистку, параллельное зачистку и параллельную компактацию. Мы официально признали Node.js как полноценного пользователя V8 наряду с Chromium. С тех пор любой патч для V8 не может быть принят, если он нарушает тестовый набор Node.js. Наша инфраструктура получила поддержку fuzz-тестирования корректности, обеспечивая, что любой кусок кода будет давать последовательные результаты независимо от конфигурации, в которой он выполняется.
В рамках масштабного отраслевого запуска V8 включил WebAssembly по умолчанию. Мы реализовали поддержку модулей JavaScript, а также полный набор функций ES2017 и ES2018 (включая асинхронные функции, общую память, асинхронную итерацию, свойства rest/spread и функции RegExp). Мы внедрили родную поддержку покрытия кода JavaScript и запустили Web Tooling Benchmark, чтобы помочь нам измерить, как оптимизации V8 влияют на производительность инструментов разработки в реальном мире и на JavaScript-код, который они генерируют. Трассировка обёртки из объектов JavaScript в объекты DOM на C++ и обратно позволила нам устранить давние утечки памяти в Chrome и эффективно обрабатывать транзитивное замыкание объектов между кучами JavaScript и Blink. Впоследствии мы использовали эту инфраструктуру для увеличения возможностей инструмента снимков состояния кучи.
В 2018 году произошло отраслевое событие, перевернувшее наши представления о безопасности информации на уровне процессоров: публичное раскрытие уязвимостей Spectre/Meltdown. Инженеры V8 провели масштабное исследование для понимания угрозы для управляемых языков и разработки мер её устранения. V8 внедрил меры защиты от Spectre и подобных атак через побочные каналы для пользователей, запускающих недоверенный код.
Недавно мы выпустили базовый компилятор для WebAssembly, названный Liftoff, который значительно уменьшает время запуска приложений на WebAssembly, сохраняя при этом предсказуемую производительность. Мы внедрили BigInt
, новый примитив JavaScript, который позволяет работать с целыми числами произвольной точности. Мы реализовали встроенные функции и сделали их возможным медленно десериализовать, существенно уменьшая объём V8 для нескольких изолятов. Мы дали возможность компилировать байткод скриптов в фоне. Мы начали проект объединённой кучи V8-Blink, чтобы выполнять синхронную сборку мусора для компонентов V8 и Blink. И год ещё не закончился…
Перепады производительности
Результаты теста V8 Bench для Chrome в течение нескольких лет показывают влияние изменений V8 на производительность. (Мы используем тест V8 Bench, поскольку это один из немногих тестов, который всё ещё может запускаться в оригинальной бета-версии Chrome.)
Наш результат в этом тесте увеличился в 4 раза за последние десять лет!
Однако вы могли заметить два понижения производительности за эти годы. Они оба интересны, так как соответствуют значительным событиям в истории V8. Снижение производительности в 2015 году произошло, когда V8 внедрил базовые версии функций ES2015. Эти функции сильно затрагивали кодовую базу V8, и мы сосредоточились на корректности, а не на производительности для их начального выпуска. Мы приняли эти незначительные регрессии скорости, чтобы как можно быстрее предоставить функции разработчикам. В начале 2018 года была раскрыта уязвимость Spectre, и V8 внедрил меры защиты, чтобы защитить пользователей от потенциальных эксплойтов, что привело к другой регрессии производительности. К счастью, теперь, когда Chrome внедряет изоляцию сайтов, мы можем снова отключить меры защиты, возвращая производительность на прежний уровень.
Еще один вывод из этого графика заключается в том, что он начинает выравниваться примерно в 2013 году. Означает ли это, что V8 сдался и перестал инвестировать в производительность? Совсем наоборот! Выравнивание графиков отражает переход команды V8 от синтетических микро-тестов (таких как V8 Bench и Octane) к оптимизации для реальной производительности. V8 Bench — это старый тест, который не использует никаких современных функций JavaScript и не приближается к реальному производственному коду. Сравните это с более новой тестовой системой Speedometer:
Хотя V8 Bench показывает минимальные улучшения с 2013 по 2018 год, наш результат Speedometer 1 за это же время вырос (еще на) 4×. (Мы использовали Speedometer 1, так как Speedometer 2 использует современные функции JavaScript, которые еще не поддерживались в 2013 году.)
В настоящее время у нас есть еще лучшее тестирование, которое точнее отражает современные приложения JavaScript, а также мы активно измеряем и оптимизируем существующие веб-приложения.
Резюме
Хотя V8 изначально был создан для Google Chrome, он всегда был автономным проектом с отдельной базой кода и API встроения, который позволяет любому приложению использовать его службы выполнения JavaScript. За последние 10 лет открытый характер проекта помог ему стать ключевой технологией не только для веб-платформы, но и в других контекстах, таких как Node.js. В процессе проект развивался и оставался актуальным, несмотря на многочисленные изменения и драматический рост.
Изначально V8 поддерживал только два набора инструкций. За последние 10 лет список поддерживаемых платформ достиг восьми: ia32, x64, ARM, ARM64, 32- и 64-битный MIPS, 64-битный PPC и S390. Система сборки V8 мигрировала от SCons к GYP и затем к GN. Проект переместился из Дании в Германию и теперь имеет инженеров по всему миру, включая Лондон, Маунтин-Вью и Сан-Франциско, а также участников вне Google из множества других мест. Мы преобразовали весь наш процесс компиляции JavaScript от безымянных компонентов к Full-codegen (базовый компилятор) и Crankshaft (оптимизирующий компилятор с обратной связью) к Ignition (интерпретатор) и TurboFan (лучший оптимизирующий компилятор с обратной связью). V8 перешел от того, чтобы быть «просто» движком JavaScript, к поддержке также WebAssembly. Сам язык JavaScript эволюционировал от ECMAScript 3 к ES2018; последний V8 даже реализует функции, появившиеся после ES2018.
История развития интернета — длинная и продолжающаяся. Отмечая 10-летие Chrome и V8, стоит помнить, что, хотя это и значительная веха, рассказ о веб-платформе длится уже более 25 лет. У нас нет сомнений, что история интернета продолжится как минимум еще столько же. Мы стремимся к тому, чтобы V8, JavaScript и WebAssembly продолжали оставаться интересными персонажами этого повествования. Мы с нетерпением ждем, что принесет нам следующий десяток лет. Следите за обновлениями!