Секрет производительности JavaScript: V8 и скрытые классы
Итак, в этой статье я расскажу о технологии, лежащей в основе такого повышения производительности, и о том, что вам следует знать, чтобы писать более производительный код.
Что такое V8 и как он работает
Содержание статьи:
V8 — это движок JavaScript с открытым исходным кодом, представленный Google. Он написан на C ++ и поддерживает веб-браузеры Google Chrome, Chromium и NodeJS. Он отвечает за взаимодействие со средой и генерацию байт-кода для запуска программ.
Первоначально V8 был представлен как механизм повышения производительности для веб-браузеров, и со временем он стал гораздо более усовершенствованным интерпретатором, чем любой другой движок.
Наиболее существенным отличием V8 от других движков является его компилятор Just In Time (JIT). JIT-компилятор компилирует весь JavaScript в машинный код во время выполнения и не генерирует какой-либо промежуточный код.
JavaScript. Быстрый старт
Изучите основы JavaScript на практическом примере по созданию веб-приложения
Как вы можете видеть на диаграмме выше, движок V8 состоит из двух основных частей. Первая часть отвечает за синтаксический анализ вашего кода, интерпретируя его в байт-код, а последняя версия V8 использует для этого процесса интерпретатор с именем Ignition . Он принимает абстрактное синтаксическое дерево (AST), сгенерированное анализатором, в качестве входных данных и генерирует байт-код.
Но все мы знаем, что компиляторы намного быстрее интерпретаторов. Тогда почему движок V8 использует интерпретатор вместо компилятора?
Основная причина использования интерпретатора Ignition — уменьшение использования памяти. Это потому, что интерпретатор будет компилировать только необходимые строки, в отличие от компилятора, который компилирует всю программу.
Однако интерпретатор Ignition отвечает только за первый запуск вашего кода. Затем сгенерированный байт-код будет использоваться компилятором под названием Turbofan. Он оптимизирует ваш код на основе данных, которые он получает во время выполнения, и перекомпилирует более оптимизированную версию.
Примечание. Хотя V8 используется для оптимизации JavaScript, он написан на C++ и использует многопоточный подход для одновременного управления всей работой.
Когда я объяснял, как работает V8, я упомянул, что интерпретатор Ignition принимает абстрактное синтаксическое дерево в качестве входных данных, поэтому давайте посмотрим, что такое абстрактное синтаксическое дерево и как оно помогает V8 улучшить производительность JavaScript.
Абстрактное синтаксическое дерево (AST)
Абстрактные синтаксические деревья используются для построения абстрактной структуры исходного кода компилятора. Кроме того, он не специализируется на JavaScript или V8; почти каждый язык программирования использует AST для преобразования представлений кода высокого уровня в представление низкого уровня.
Когда вы конвертируете свой код в AST, он будет включать необходимые детали кода, такие как типы переменных, расположение, порядок операторов и т. д. Таким образом, вашему компилятору не придется иметь дело с ненужными вещами, такими как комментарии.
Чтобы лучше понять, возьмем простой код JavaScript и сгенерируем для него AST:
JavaScript // Function declaration function addition(x, y){ var answer = x + y; console.log(answer); } // Calling the function addition(10,20);
12345678 | // Function declarationfunction addition(x, y){ var answer = x + y; console.log(answer);} // Calling the functionaddition(10,20); |
Затем я использовал инструмент онлайн-анализа, предоставленный esprima, чтобы сгенерировать AST для этого кода. В следующем фрагменте кода показана часть AST, вы можете найти полный AST кода здесь.
JavaScript { “type”: “Program”, “body”: [ { “type”: “FunctionDeclaration”, “id”: { “type”: “Identifier”, “name”: “addition” }, “params”: [ { “type”: “Identifier”, “name”: “x” }, { “type”: “Identifier”, “name”: “y” } ], “body”: { “type”: “BlockStatement”, “body”: [ … ], “kind”: “var” }, … “sourceType”: “script” }
1234567891011121314151617181920212223242526272829 | { “type”: “Program”, “body”: [ { “type”: “FunctionDeclaration”, “id”: { “type”: “Identifier”, “name”: “addition” }, “params”: [ { “type”: “Identifier”, “name”: “x” }, { “type”: “Identifier”, “name”: “y” } ], “body”: { “type”: “BlockStatement”, “body”: [ … ], “kind”: “var” }, … “sourceType”: “script”} |
AST определяет пары значений ключа для каждой строки кода. Идентификатор определяет, что AST принадлежит программе, а затем все строки кода будут определены внутри тела, которое представляет собой массив объектов.
Как я уже упоминал, все объявления функций, объявления переменных, имена, типы организованы построчно, а комментарии не учитываются.
Помимо процесса оптимизации и использования AST, V8 использует еще один прием для повышения производительности JavaScript. Итак, давайте разберемся, что это такое и как работает.
Скрытые классы для оптимизации кода JavaScript
Как мы все знаем, JavaScript — это язык динамических типов. Это означает, что мы можем добавлять или удалять атрибуты объектов на лету.
JavaScript. Быстрый старт
Изучите основы JavaScript на практическом примере по созданию веб-приложения
Однако этот подход требует более динамического поиска, что снижает производительность JavaScript. Механизмы V8 используют скрытые классы для решения этой проблемы и оптимизации выполнения JavaScript.
Как работают скрытые классы
Когда вы создаете новый объект, движок V8 создаст для него новый скрытый класс. Затем, если вы измените тот же объект, добавив новое свойство, механизм V8 создаст новый скрытый класс со всеми свойствами из предыдущего класса и включит новое свойство.
Давайте снова возьмем приведенный выше пример и посмотрим, как генерируются скрытые классы. Итак, когда я создаю пустой объект ( const userObject = {}), V8 создаст соответствующий скрытый класс (C01).
Затем я изменю этот объект, добавив новое свойство. ( userObject.name = «Chameera»). Теперь механизм V8 создаст новый скрытый класс (C02), унаследовав все свойства от предыдущего скрытого класса (C01).
Это позволит компилятору обойти поиск в словаре при обращении к имени свойства, а V8 будет напрямую указывать на класс C01.
Если я добавлю к этому объекту еще одно свойство, произойдет тот же процесс. Будет создан еще один скрытый класс, который будет иметь как предыдущие, так и новые атрибуты.
Эта концепция скрытого класса позволяет не только обходить поиск в словаре; она также позволяет повторно использовать уже созданные классы при создании или изменении похожих объектов.
Например, если вы создадите другой пустой объект с именем article (const articleObject = {}), механизм V8 не создаст новый скрытый класс. Вместо этого он будет указывать на уже созданный класс C01.
Но если вы измените articleObject, добавив новое свойство с именем articleName, V8 не сможет использовать ранее созданный класс (C02), поскольку у него есть только свойство с именем name.
Написание высокопроизводительного кода JavaScript
Итак, если вы хотите максимизировать производительность своего кода JavaScript, вам может потребоваться уменьшить добавление динамических свойств.
Предположим, вы запускаете цикл в NodeJS. Если вы добавите динамические свойства для объекта, вы увидите разницу в производительности внутри цикла. Поэтому лучше создавать свойства вне цикла и использовать их вместо того, чтобы добавлять их динамически внутри цикла.
Когда V8 повторно использует существующие скрытые классы, он будет работать намного лучше.
Вывод
Всякий раз, когда обсуждают, как работает JavaScript, мы говорим о циклах событий, микрозадачах, макро-задачах и очередях обратного вызова. Однако все это не реализовано в JavaScript. Вместо этого они являются частью движка V8 и отвечают за оптимизацию вашего кода JavaScript.
Вот почему я хотел обсудить, как работает V8 и как он использует концепцию скрытых классов для оптимизации вашего кода.
Я надеюсь, что вы узнали что-то новое о JavaScript из этой статьи. Спасибо за чтение!!!
Автор: Chameera Dulanga
Источник: webformyself.com