Релиз V8 v7.8
Каждые шесть недель мы создаем новую ветку V8 в рамках нашего процесса релиза. Каждая версия создается из основной ветки Git перед бета-вехой Chrome. Сегодня мы рады объявить о нашей новой ветке, V8 версии 7.8, которая находится в бета-версии до выпуска вместе с Chrome 78 Stable через несколько недель. V8 v7.8 наполнен полезными инструментами для разработчиков. В этом посте мы предлагаем краткий обзор некоторых основных моментов перед релизом.
Производительность JavaScript (размер и скорость)
Потоковая загрузка скриптов при предварительной загрузке
Возможно, вы помните нашу работу по потоковой загрузке скриптов в V8 v7.5, где мы улучшили фоновую компиляцию, чтобы считывать данные напрямую из сети. В Chrome 78 мы включаем потоковую загрузку скриптов во время предварительной загрузки.
Ранее потоковая загрузка скриптов начиналась при обнаружении тега <script>
во время разбора HTML, и разбор либо останавливался до завершения компиляции (для обычных скриптов), либо скрипт выполнялся по завершении компиляции (для асинхронных скриптов). Это значит, что для обычных, синхронных скриптов, как этот:
<!DOCTYPE html>
<html>
<head>
<script src="main.js"></script>
</head>
...
…поток раньше выглядел примерно так:
Так как синхронные скрипты могут использовать document.write()
, нам приходится останавливать разбор HTML при обнаружении тега <script>
. Так как компиляция начинается при обнаружении тега <script>
, существует значительный разрыв между разбором HTML и непосредственным выполнением скрипта, во время которого страница не может продолжать загружаться.
Однако мы также обнаруживаем тег <script>
на более ранней стадии, когда сканируем HTML на наличие ресурсов для предварительной загрузки, так что процесс на самом деле был больше похож на это:
Можно с достаточной уверенностью предположить, что если мы предварительно загружаем файл JavaScript, то в конечном итоге захотим его выполнить. Таким образом, начиная с Chrome 76, мы экспериментировали с потоковой загрузкой при предварительной загрузке, где загрузка скрипта также запускает его компиляцию.
Еще лучше, так как мы можем начать компиляцию до завершения загрузки скрипта, процесс при потоковой загрузке теперь выглядит более следующим образом:
Это значит, что в некоторых случаях мы можем уменьшить заметное время компиляции (промежуток между обнаружением тега <script>
и началом выполнения скрипта) до нуля. В наших экспериментах это заметное время компиляции сократилось в среднем на 5–20%.
Самая лучшая новость заключается в том, что благодаря нашей инфраструктуре экспериментов мы смогли не только включить это по умолчанию в Chrome 78, но и активировать для пользователей Chrome 76 и более поздних версий.
Более быстрая деструктуризация объектов
Деструктуризация объектов в форме…
const {x, y} = object;
…почти эквивалентна переписанной форме...
const x = object.x;
const y = object.y;
…за исключением того, что она должна выбрасывать особую ошибку, если object
равен undefined
или null
...
$ v8 -e 'const object = undefined; const {x, y} = object;'
unnamed:1: TypeError: Cannot destructure property `x` of 'undefined' or 'null'.
const object = undefined; const {x, y} = object;
^
…в отличие от обычной ошибки, которую вы получили бы при попытке разыменовать undefined
:
$ v8 -e 'const object = undefined; object.x'
unnamed:1: TypeError: Cannot read property 'x' of undefined
const object = undefined; object.x
^
Эта дополнительная проверка делала деструктуризацию медленнее, чем простое присваивание переменной, как нам сообщили через Twitter](https://twitter.com/mkubilayk/status/1166360933087752197).
Начиная с версии V8 v7.8, деструктуризация объектов такая же быстрая, как эквивалентное присваивание переменной (фактически, мы генерируем одинаковый байт-код для обоих случаев). Теперь вместо явных проверок на undefined
/null
, мы полагаемся на выбрасывание исключения при загрузке object.x
и перехватываем исключение, если оно возникает в результате деструктуризации.
Ленивые позиции исходного кода
При компиляции байт-кода из JavaScript создаются таблицы позиций исходного кода, которые связывают последовательности байт-кода с позициями символов в исходном коде. Однако эта информация используется только при отображении исключений или выполнении задач разработчика, таких как отладка и профилирование, поэтому в основном она занимает память впустую.
Чтобы избежать этого, теперь мы компилируем байт-код без сбора позиций исходного кода (если не подключены отладчик или профилировщик). Позиции исходного кода собираются только тогда, когда фактически генерируется трассировка стека, например, при вызове Error.stack
или печати трассировки стека исключения в консоль. Это имеет некоторую стоимость, так как для генерации позиций исходного кода требуется повторный парсинг и компиляция функции, однако большинство веб-сайтов не отображают трассировки стека в продуктивной среде, и поэтому не наблюдают какого-либо заметного воздействия на производительность. В наших лабораторных тестах мы увидели снижение использования памяти V8 на 1-2,5%.
Быстрее определение неудачных совпадений в RegExp
Обычно RegExp пытается найти совпадение, проходя вперед по входной строке и проверяя совпадение, начиная с каждой позиции. Как только эта позиция оказывается достаточно близко к концу строки, чтобы совпадение стало невозможным, V8 теперь (в большинстве случаев) прекращает попытки находить возможные начала новых совпадений и вместо этого быстро возвращает неудачу. Эта оптимизация применима как для скомпилированных, так и для интерпретируемых регулярных выражений и обеспечивает ускорение для нагрузок, где неудачи в поиске совпадения распространены, а минимальная длина любого успешного совпадения относительно велика по сравнению с средней длиной входной строки.
В тесте UniPoker из JetStream 2, который вдохновил на эту работу, V8 v7.8 обеспечивает улучшение на 20% среднего значения всех итераций.
WebAssembly
API WebAssembly C/C++
Начиная с версии v7.8, реализация API Wasm C/C++ API в V8 выходит из экспериментального статуса и становится официально поддерживаемой. API позволяет использовать специальную сборку V8 в качестве движка исполнения WebAssembly в ваших приложениях на C/C++. Без участия JavaScript! Дополнительные детали и инструкции смотрите в документации.
Улучшенное время запуска
Вызов функции JavaScript из WebAssembly или функции WebAssembly из JavaScript требует выполнения некоторого вспомогательного кода, отвечающего за перевод аргументов функции из одного представления в другое. Генерация этого вспомогательного кода может быть довольно затратной: в демо Epic ZenGarden компиляция вспомогательных функций занимает около 20% времени запуска модуля (компиляция + инстанцирование) на машине с 18 ядрами Xeon.
Для этого релиза мы улучшили ситуацию, более эффективно используя фоновые потоки на многопроцессорных системах. Мы опирались на недавние усилия по масштабированию компиляции функций и интегрировали компиляцию вспомогательных функций в этот новый асинхронный конвейер. Компиляция вспомогательного кода теперь занимает около 8% времени запуска демо Epic ZenGarden на той же машине.
API V8
Пожалуйста, используйте git log branch-heads/7.7..branch-heads/7.8 include/v8.h
, чтобы получить список изменений API.
Разработчики с активной копией исходного кода V8 могут использовать git checkout -b 7.8 -t branch-heads/7.8
, чтобы опробовать новые функции в V8 v7.8. Альтернативно вы можете подписаться на бета-канал Chrome и скоро попробовать новые функции самостоятельно.