[Перевод] Основы движков JavaScript: общие формы и Inline кэширование. Часть 2
Всем привет! Курс «Безопасность информационных систем» стартует уже через 2 недели, поэтому сегодня мы хотим опубликовать вторую часть статьи, публикация которой приурочена к его запуску. Прочитать первую часть можно тут. Итак, начнем.
Inline Caches (ICs)
Основной идеей, которая стоит за формами, является концепция inline кэшей или ICs. Они являются ключевым компонентом быстрой работы JavaScript! Движки JavaScript используют ICs для запоминания информации о том, где найти свойства объектов, чтобы уменьшить количество затратных поисков.
У нас есть функция getX
, которая принимает на вход объект и загружает из него свойство x
:
function getX(o) {
return o.x;
}
Если мы запустим эту функцию в JSC, то получим следующий байткод:
Первая инструкция get_by_id
загружает свойство ‘x’
из первого аргумента (arg1)
и хранит результат в loc0
. Следующая инструкция возвращает то, что мы хранили в loc0
.
JSC также встраивает inline Cache в инструкцию get_by_id
, которая состоит их двух неинициализированных слотов.
А теперь давайте будем считать, что мы вызываем getX
вместе с объектом { x: 'a' }
. Мы уже знаем, что этот объект имеет свойство ‘x’
, а его форма хранит смещение и атрибуты свойства х
. Когда вы выполняете функцию в первый раз, инструкция get_by_id
ищет свойство ‘x’
и обнаруживает, что его значение хранится в смещении 0.
Встроенный в инструкцию get_by_id
IC запоминает форму и смещение, где было найдено свойство.
Для последующих запусков IC нужно только сравнивать форму, и если она такая же, как и раньше, просто загружать значение из запомненного смещения. В частности, если движок JavaScript видит объекты с формой, которую он записал ранее, ему больше не нужно обращаться за информацией об этих свойствах вообще — вместо этого дорогостоящий поиск информации о свойствах можно полностью пропустить. Это значительно быстрее, чем каждый раз тратить время на поиск свойства.
Эффективное хранение массивов
Для массивов общей практикой является хранение индексов массива. Значения таких свойство называются элементами массива. Было бы расточительно хранить атрибуты свойств для каждого элемента массива в отдельном массиве. Вместо этого движки JavaScript опираются на тот факт, что свойства, индексируемые в массиве доступны для записи (writable), перечисления (enumerable) и настройки (configurable) по умолчанию, а также хранят элементы массива отдельно от других именованных свойств.
Рассмотрим приведенный массив:
const array = [
'#jsconfeu',
];
Движок хранит массив единичной длины и указывает на форму, которая содержит смещение и атрибуты для свойства ‘length’
.
Это похоже на то, что мы уже видели раньше… Но где же хранятся значения элементов массива?
Каждый массив имеет отдельное хранилище резервных копий элементов (elements backing store), содержащее все значения свойств, индексированных массивом. Движку JavaScript не нужно хранить никаких атрибутов свойств для элементов массива, поскольку они обычно доступны для записи (writable), перечисления (enumerable) и настройки (configurable).
А что будет если они вдруг окажутся недоступными для конфигурирования? Что если вы измените атрибуты свойство элементов массива?
// Please don’t ever do this!
const array = Object.defineProperty(
[],
'0',
{
value: 'Oh noes!!1',
writable: false,
enumerable: false,
configurable: false,
}
);
Отрывок кода сверху определяет свойство, которое называется ‘0’
(при этом оно оказывается индексом массива), оно изменяет значения атрибутов на недефотлные.
В таких крайних случаях движок JavaScript представляет все резервное хранилище элементов как словарь, который сопоставляет индексы массива с атрибутами свойств.
Даже если всего один элемент массива имеет недефолтные атрибуты, все хранилище резервных копий элементов переходит в медленный и неэффективный режим работы. Избегайте Object.defineProperty
в индексах массива! (Я даже не знаю зачем вам в принципе это использовать. Это кажется странным и нерациональным.)
Выводы
Мы узнали, как движки JavaScript хранят объекты и массивы, как формы и inline кэши помогают оптимизировать различные операции. Также в этой статье мы хотим дать несколько практических подсказок для JavaScript, которые могут помочь увеличить производительность вашего кода:
- Всегда инициализируйте свои объекты одинаково, чтобы они не имели различных форм;
- Не связывайтесь с атрибутами свойств элементов массива, дайте им возможность спокойно храниться и эффективно работать.
Вот теперь статью можно считать завершенной. По устоявшейся традиции ждём ваши комментарии и приглашаем записаться на открытый вебинар по курсу «Безопасность информационных систем», который уже сегодня проведет известный вирусный аналитик и по совместительству наш преподаватель — Александр Колесников.
Прочитать первую часть.