Автор этой статьи оптимизирует соответствующие реализации в бизнесе документов Tencent и оптимизирует производительность сценариев высокочастотных вызовов в десять раз по сравнению с исходным значением, сокращая при этом затраты времени на индексацию основного документа на 10–15%. в памяти все еще настолько тонок, что его можно не учитывать. Эта статья начнется с общей архитектуры V8, даст подробное объяснение объектной модели V8, укажет детали и принципы оптимизации микросхем из деталей сборки и, наконец, напишет сверхбыстрый JS-код на основе этих принципов оптимизации 🚀
Следите за разработчиками Tencent Cloud и заранее получайте техническую информацию из первых рук👇
js-код от исходного кода до исполнения — конвейер компилятора v8:
парсер компилирует исходный код в AST и компилирует его в «байт-код байт-кода» на основе AST.
Ignition — это интерпретатор байт-кода v8. Он может запускать байт-код и постоянно собирать «обратную связь» или зеленые линии во время выполнения процесса и передавать его в TurboFan для окончательной компиляции и оптимизации машинного кода.
Поскольку js — очень динамичный язык, скомпилированные «машинные инструкции» могут быть некорректными, поэтому во время работы может потребоваться откат к интерпретатору зажигания. Эти проблемы передаются обратно в интерпретатор зажигания через «красные линии». Этот процесс называется «деоптимизация».
—— Более конкретно:
2.1 Уровень выполнения кода: переход от исходного кода к байт-коду и машинному коду на самом деле представляет собой процесс непрерывной компиляции.
В мире много мест, где может выполняться код. На числовой прямой есть две крайности: человеческий мозг с самым высоким уровнем абстракции слева и процессор с самым низким уровнем абстракции справа:
Три объекта на рисунке выше понимают следующий код с разных точек зрения. От исходного кода к байт-коду, а затем к машинному коду — это на самом деле процесс непрерывной компиляции в другой язык.
const a = 3 + 4;
2.1.1 Понимание человеческого мозга
Вычислите 3+4 и сохраните его в переменной js const a.
2.1.2 Понимание парсера V8
Разберите код в дерево AST (структуру JSON).
2.1.3 Понимание зажигания V8
Ignition скомпилирует код в байт-код:
...
LdaSmi [3] // Загрузить литерал 3 Перейти к вершине стека
Star0 // вершина стека 3 pop зарегистрироваться r0
Add r0, [4] // вычислить r0 + 4
...
2.1.4 Общие сведения о V8 TurboFan
TurboFan воспримет код как ассемблер:
...
mov ax 3 # Воля 3 Назначениезарегистрироваться ax
add ax 4 # вычислить ax = ax + 4
...
2.2 По сути байт-код и сборка x86 — это одно и то же.
По сути, байт-код v8 и сборка x86 одинаковы, за исключением того, что в мире не существует голой машины, которая могла бы запускать байт-код, понятный v8. Причина, по которой машинный код быстрый, заключается в том, что ЦП может запускать голую сборку на аппаратном уровне. , так что это очень быстро.
Короче говоря, чтобы полностью выразить динамические характеристики js и облегчить оптимизацию сборки, которую можно запускать непосредственно процессором, в v8 введен уровень байт-кода, который ближе к физической машине, чем AST, поскольку в нем нет иерархической вложенности и представляет собой набор команд на основе регистров.
2.3 Сроки компиляции: JIT/AOT
JIT относится к технологии компиляции, которая оптимизирует машинный код во время работы. К представителям относятся jvm/lua jit/v8. Этот тип технологии оптимизации будет продолжать собирать информацию о выполнении и оптимизировать производительность программы во время ее выполнения. AOT относится к традиционному поведению компиляции, которое широко используется в статически типизированных языках (таких как C, C++, Rust) и некоторых динамически типизированных языках (таких как Go, Swift), поскольку полный код можно увидеть заранее. , компилятор/ Среда выполнения языка может выполнять достаточную оптимизацию на этапе компиляции для повышения производительности программы.
Поскольку язык JIT не может анализировать код и заранее оптимизировать выполнение, «период компиляции» языка JIT очень мал, а «время выполнения» довольно велико. Многие оптимизации компиляции реализуются во время выполнения кода.
2.4 Зажигание и байт-код
Ignition отвечает за интерпретацию и выполнение байт-кода промежуточного уровня, представленного V8, который связан со спецификацией js в человеческом мозге и базовыми машинными инструкциями ЦП.
2.5 TurboFan и машинный код
TurboFan может компилировать байт-код в самый быстрый машинный код, позволяя запускать «голое железо» напрямую для достижения максимальной скорости выполнения.
Используйте этот параметр, чтобы включить внедрённый вызов среды выполнения v8 для анализа и отладки v8.
# node Включать
$ node --allow-natives-syntax
# chrome Включать
$ open -a Chromium --args --js-flags="--allow-natives-syntax"
Ниже приведены некоторые часто используемые инструкции.
3.1 %DebugPrint(something);
Вы можете распечатать внутреннюю информацию об объекте в v8, например, распечатать функцию:
3.2 %OptimizeFunctionOnNextCall(fn);
Скажите v8, чтобы он активно запускал функцию оптимизации fn при следующем вызове.
3.3 %GetOptimizationStatus(fn);
Получите текущий статус оптимизации функции, который будет подробно описан позже:
Соответствует этому перечислению в исходном коде V8:
С точки зрения разработки функция является оптимальной. status должно быть 00000000000001010001 (81)
Прямо сейчас.
3.4 %HasFastProperties(obj);
%HasFastProperties можно использовать для вывода информации о том, находится ли объект в режиме быстрых свойств.
Эти быстрые свойства и противоположные им медленные свойства будут представлены позже.
Прежде всего, Tagged Pointer — это широко используемая технология оптимизации в C/C++. Она полезна не только в V8. В частности, она определяет поведение указателя на основе определенных битов его собственного значения. Этот тип указателя заключается в том, что «его указатель. Определенные биты числового значения имеют особое значение».
Например, в v8 указатели кучи js и небольшие целочисленные типы SMI (маленькие целые числа) выражаются и на них ссылаются через Tagged Pointer. Разница заключается в том, равен ли младший бит 0 для определения типа указателя:
Указатель объекта (32 бита):
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxx1
Маленькое целое число SMI (32 бита), где часть xxx — это числовая часть:
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxx0
Выраженный на языке C, это:
#include <stdio.h>
void printTaggedPointer(void * p) {
// Поверните его с силой, сосредоточиться на p само по себе числовое значение
unsigned int tp = ((unsigned int) p);
if ((tp & 0b1) == 0b0) {
printf("p да SMI, Числовое значение 0x%x \n", tp >> 1);
return;
}
printf("p даStackобъект указатель, Object<0x%x> \n", tp);
// printObject(*p); // Предположим, что у вас есть индивидуальный метод, который может печатать объект кучи.
}
int main() {
printTaggedPointer(0x1234 << 1); // smi
printTaggedPointer(17); // object
return 0;
}
Эффект операции:
Примечание:
void *
да C из любые, есть много принудительных переводов, пожалуйста, игнорируйте warning;Давайте сначала рассмотрим этот пример: функцию add(x,y). Если во время работы передается несколько типов параметров, код станет медленнее:
Мы видим, что L15 работает намного медленнее, в несколько раз медленнее, чем первоначальные 66 мс.
причина:
5.1 Не произойдет ли сбой/аппаратная ошибка/ошибка сегментации, если предположение будет нарушено?
Например, вначале передается число, но когда дело доходит до оптимизированного кода, используется ассемблерная инструкция add; когда передается строка или другое допустимое JS-значение, действительно ли нет проблем с выполнением добавления; функция скомпилирована в сборку? —— Проблем не будет, потому что TurboFan внесет множество контрольных точек в скомпилированный «машинный код». Фактически, эти контрольные точки выполняют проверку типов, если типы не совпадают, вызов будет немедленно прекращен и. «Антиоптимизация» позволяет Ignition интерпретировать и выполнять через байт-код.
Приведенное выше утверждение может быть расплывчатым. Мы можем более подробно рассмотреть, как выглядит типизированная сборка. Распечатать оптимизированную сборку можно следующими способами. x86 Сборка (м1 Чип от Apple Computer должен быть arm инструкция).
$ node --print-opt-code --allow-natives-syntax --trace-opt --trace-deopt ./a.js
Как показано на рисунке ниже, реализация тестовой функции заключается в добавлении 0x1234 к первой записи и ее возврате, и эта основная логика соответствует строке сборки L37, а остальные части, за исключением собственного «соглашения о вызовах» v8, являются контрольными точками. проверки типа и некоторые точки останова отладки:
Из предыдущего обсуждения тегированного указателя мы можем знать, что L19 ~ L22 фактически определяют, является ли входной параметр SMI, в частности, [rbx+0xf] и 0x1 выполняют побитовую операцию И ([rbx+0xf] передается через параметр стека , что является соглашением о вызовах js в v8) Если результат равен 0, перейдите к 0x10b7cc34f, что является последующим обычным процессом, в противном случае перейдите к CompileLazyDeoptimizedCode использует интерпретатор байт-кода для выполнения процесса деоптимизации. Здесь я написал сравнение псевдокода дизассемблирования:
Кроме того, мы также видим, что основная логика соответствует только одной строке ассемблера, а остальные инструкции являются либо контрольными точками, либо соглашением о вызовах v8/js. При таком большом количестве избыточных инструкций производительность выполнения по-прежнему очень высока. Видно, что эффективность выполнения сборки значительно выше, чем у построчных интерпретаторов.
5.2 Где я могу распечатать так называемый отзыв?
Это можно увидеть через %DebugPrint.
Когда это предположение нарушено, оно станет Any:
5.3 Приведет ли полиморфный возврат к снижению эффекта оптимизации?
Не будет.
5.4 Что такое {Mono|Poly|Mega|}морфный в слоте обратной связи?
Согласно упомянутым ранее контрольным точкам, у трех вышеперечисленных моно есть меньше всего контрольных точек, а последний мега будет очень большим, с худшей производительностью оптимизации, или V8 просто не будет выполнять более глубокую оптимизацию машинного кода для таких функций (таких как следующие микросхемы, упомянутые в этой статье).
5.5 Насколько трудоемким является сам процесс TurboFan?
Компиляция из JS AST/байт-кода в машинный код также требует накладных расходов порядка миллисекунд.
5.6 Что делать, если я деоптимизировал слишком много раз?
Согласно этой статье V8 function optimization - Blog by Kemal Erdem Если функция «деоптимизирована» 5 Спустя время v8 Эта функция больше не будет оптимизирована в будущем, но я не могу воспроизвести описанную им ситуацию. Возможно, это старая версия. v8 Производительность узла 16 Такого не будет, несмотря ни на что, пока run Достаточно раз турбовентиляторный, просто если «тип параметра, однажды переданный, слишком union typed» приведет к очень большим потерям в эффектах оптимизации.
5.7 Когда будет запущен TutboFan?
Мы уже знаем, что «выполнение достаточного количества раз» вызовет оптимизацию, и это лишь один из случаев. Подробности см. в реализации MustOptimize в версии 8, где подробно описано, когда начинать оптимизацию:
С точки зрения развития:
Примечание:maglev Это было в прошлом году chrome v8 Новые функции, разработанные командой —— Оптимизация уровня компиляции обычно основана на feedback Точно контролируйте уровень компиляции машинного кода для достижения лучших результатов оптимизации. Следующий рисунок: v8 Опубликовано командой benchmark контраст:
Подробную информацию см. v8.dev/blog/maglev
5.8 Будет ли скомпилированный код занимать память?
Да, и иногда эта часть памяти занимает много. Это одна из важных причин, по которой Chrome часто высмеивают как убийцу памяти. Если взять в качестве примера qq.com, то конкретное соответствие заключается в том, что (скомпилированный код) находится в. heapdump содержит скомпилированный код. Использование памяти кода:
Начало этого раздела является ключевой частью статьи, поскольку только понимая структуру памяти объектов V8, мы можем по-настоящему понять причины многочисленных оптимизаций V8.
6.1 Как структура языка C реализует «чтение точек»?
Прежде чем официально приступить к делу, давайте сначала посмотрим, как осуществляется «чтение по щелчку» структуры в C.
C воля struct понимается как непрерывная линейная buffer структура,И существование делится по полю из типа от индекса из какого внутри какого внутрида какого индивидуального поля (выравнивание),поэтомусуществоватькомпилировать point.x будет изменен на base+4 Доступ к свойству осуществляется следующим образом, как показано на рисунке ниже. Временная сложность равна. O(1) из:
Таким образом, C не предоставляет метода для получения значения структуры из имени ключа поля, то есть точка['x'] не поддерживается. В этом случае вам нужно написать свой собственный метод получения для выполнения подобных операций.
Этот тип технологии получения значений из объектов на основе строковых значений обычно встроен в современные языки программирования. Обычно он называется отражением и позволяет получать доступ к информации исходного кода во время выполнения.
Но в JS объекты являются динамическими и могут иметь любое количество значений «ключ-значение», и эти пары «ключ-значение» kv также могут динамически меняться во время выполнения. Например, я могу p.xxx =123 или удалить p.xxx в любое время. Чтобы удалить его, это означает, что «формы» объекта и его «структура памяти» не могут быть проанализированы статически, и эта структура памяти не должна быть «фиксированной по длине» и требует динамического malloc для изменения длины.
Предположим, что на дворе 2008 год, и вы инженер Google, разрабатывающий команду проекта Chrome v8. Как бы вы спроектировали структуру памяти для объектов JS?
const obj = { x: 3, y: 5 }
// obj Как можно спроектировать структуру памяти?
Один взгляд на Дин Чжэня, и он начал:
Определение ключа добавляет значение, а затем структуру kv объекта можно выразить путем массивирования этой структуры. Добавление атрибутов будет продолжать расширяться позже. Алгоритм поиска от начала до конца, а временная сложность равна O (n).
Но если мы последуем этому дизайну, следующие два объекта будут иметь повторяющиеся определения ключей и потреблять память:
const obj1 = { x: 11, y: 22 } // "x" 11 "y" 22
const obj1 = { x: 33, y: 44 } // "x" 33 "y" 44
// повторю "x" и "y"
Хорошо, просто сделайте описанную выше простую вещь, и это вызовет много проблем. Начиная снизу, см. ниже, как V8 описывает объекты.
6.2 JSObject и named-properties & indexed-elements
существовать js стандартный Array это особый тип Объект, но из соображений производительности V8 Ориентирован на нижнюю частьобъектимножествоизиметь дело сдадругойиз:
Как показано на рисунке ниже, JSObject:
существовать V8 внутри:
Эм? Формы предметов? Что это такое?
6.3 Формы предметов
так называемый объект формирует, собственно, то, что находится на объекте ключ, упомянутый ранее V8 Изоптимизация требует постоянного сбора данных во время работы. обратная связь, например, когда выполняется следующий код, движок может знать «obj есть два ключ, один a один b」:
const obj = {}
obj.a = 123;
obj.b = 124;
doSomething(obj);
V8 проходить Hidden Class структура для записи JSObject существуютвремя выполненияиз временииметь, которое ключ, который является объектом записи формы, из-за JSObject Он динамичен и может быть установлен по желанию позже. obj.xxx = 123, то есть объект shapes изменится,Поэтомуобъектдержатьиметьиз Hidden Class Будет меняться по мере запуска конкретного кода.
Hidden Class да Сравнительное академическое утверждение, существующее V8 «Имя проекта» в исходном коде: Map,существовать Майкрософт Edge Chakra (edge) Это называется Types,существовать JavaScriptCore (WebKit Safari) Это называется Structure,существовать SpiderMonkey (FireFox) Это называется Shapes .... Короче говоря, все основные движки реализовали отслеживание «объектов». shapes изменять".
Следующие выше термины могут сбить с толку: все они относятся к скрытому классу, используемому для описания формы объектов.
6.4 Hidden Class DescriptorArrays и in-object properties
Как уже говорилось ранее, помимо *properties и *elements Может использоваться для хранения других членов объекта, JSObject. Он также обеспечивает так называемую in-object properties из Способы хранения членов объекта,То естьда Воляобъектсохранение участникасуществовать「JSObject «структура» и соответствие Hidden Class Опишите ключевое значение:
На рисунке выше под скрытым классом находится подструктура DescriptorArrays. Эта структура записывает ключ члена объекта и соответствующий ему индекс, хранящийся в объекте, который показан фиолетовым прямоугольником выше.
Возможно, вы спросите:
6.5 Изменение скрытого класса
Если скрытый класс статичен, то для описания скрытого класса достаточно этой картинки:
Но объект shapes изменится, и, следовательно, объект сохранится Hidden Class будет меняться по мере запуска конкретного кода, V8 использовал Transition Цепочка — метод, основанный на построении связанного списка для описания «изменения Hidden Class」:
Примечание:длячтобы облегчить обсуждение,Более поздний текствозможный Нетволя Hidden Class Нарисуйте его как связанный список, но сведите его вместе и опустите пустые объекты. формы, дополнительно Hidden Class Node На поле есть и другие поля, которые относительно менее важны, поэтому они игнорируются.
Из-за особенностей связанных списков объектам одинаковой формы, очевидно, легче повторно использовать один и тот же скрытый класс. Например, в следующем случае o1 и o2 повторно используют узел скрытого класса с адресом 0xABCD:
При наличии разных направлений в это время будет открыто отдельное. branch Чтобы описать эту ситуацию, в настоящее время o1 и o2 Оно никогда не будет прежним:
6.6 Обзор объектной модели V8
Из предыдущего обсуждения мы можем сделать следующие выводы:
Вопросы без ответа:
Пожалуйста, перенесите эти два вопроса в следующую главу «Встроенные кэши» и продолжайте читать.
После введения скрытого класса, чтобы прочитать определенный член, вам нужно проверить скрытый класс, чтобы получить индекс внутри объекта. Разве этот процесс все еще O(n)?
Да, чтение членов, не зная заранее формы JSObject, действительно является O(n), но я уже упоминал об этом раньше:
V8 Многие оптимизации основаны на assumption из,Таксуществовать известно obj из Shapes в данной ситуации, что бы вы сделали для оптимизации ниже этого человека distance функция?
таким образомоптимизация Сразу Воляпроходить key Посещение участников O(n) Процесс оптимизирован, т. O(1) Читайте напрямую, нажав смещение индекса. Этот метод оптимизации называется. Inline Caches (ИС), что-то вроде C Язык из struct Скомпилируйте чтение точек поля в доступ к смещению, но эта компиляция JIT из,Неа C таким образом AOT статическийкомпилировать Конечноиз,да V8 существоватьфункцииосуществлять много раз, собранные достаточно из feedback Позже понял из.
Вы также можете спросить: distance2 Как определить входящий из когда из p1 p2 из shapes Есть ли изменения? Помните предыдущее? 0xABCD ?Это верно,компилироватьназадизкомпиляция checkpoint Просто судите о входящем объектизе непосредственно по да hidden classs Значение указателя 0xABCD, если нет, просто активируйте «антиоптимизацию» и запустите режим интерпретатора.
—— В следующем примере шаг за шагом будет представлено ICs из Реальные сцены и детали компиляции.
7.1 Пример сборки: почему статическое из лучше динамического ?
спереди Inline Cache Из обсуждения можно узнать, что необходимо подтвердить визит key могу это сделать ICs оптимизация, поэтому при написании кодиз, например, старайтесь избегать следующих приемов: key string Динамический поиск свойств объекта:
function test(obj: any, key: string) {
return obj[key];
}
Если вы можете ясно знать key изконкретное значение,В это время рекомендуется писатьдля:
function test(obj: any, key: 'a' | 'b') {
if (key === 'a') return obj.a;
if (key === 'b') return obj.b;
}
Даже если вам нужно выполнять динамический запрос, но вы знаете, что определенный подпрограмма case Оккупированный 99% изнастраиватьиспользоватьчастота,в это время ХОРОШОэтот Образецоптимизация:
function test(obj, key: 'a' | 'b') {
// для 'a' изнастраиватьиспользоватьчастота Оккупированный 99% Вы можете продвигаться по такой оптимизации
if (key === 'a') return obj.a;
return obj[key];
}
Два стиля письма — статический и динамический — могут отличаться в несколько или даже сотни раз.,если бизнес внутриимеет большие миллионы раз, используйте тест,оптимизация может сэкономить много миллисекунд,например, следующий пример «упрощенного обнаружения сервисов» показывает разрыв почти в сто раз:
Причина в том s2.js Все эти атрибуты, доступные в, доступны ICs Технология, оптимизированная для O(1) Посетили, очень быстро —— для Понятно Исследовать Внутриотделениеиз ICs Связанная логика сборки, попробуйте вывести serviecMap из Hidden Class (V8 внутри hidden class Псевдоним Map) И исходный код сборки:
первый %DebugPrint вне serviceMap из Hidden Class из физического адреса, вы можете увидеть да 0x3a8d76b74971 Тогда смотрите продолжение компилироватьоптимизацияиз arm machine code Как использовать этот адрес для достижения ICs технологияоптимизацияиз: (Автор будет изкомпьютера да mac m1 поэтому это arm Компиляция, не x86 компиляция).
Как видно, микросхемы оптимизация Пост-компиляция checkpoint Фактически, это будет Hidden Map из указателя физического адреса напрямую Inline приезжатькомпиляциявнутри Понятно,способ проверки гипотез,Однаконазад Сразу Можетпрямой Воля Доступ к собственностиоптимизациядля O(1) из in-object properties посещено, поэтому эта технология называется Inline Cahce (ICs) .
(это почти V8 внутри Лучший эффект изоптимизация, Поэтому часть benchmark внутри nodejs Объект может быть более Java Объект также быстрый, потому что Java внутрииметь может чрезмерно использовать отражение, что приведет к очень низкой производительности объекта).
7.2 Fast Properties и Slow Properties
если бы знал ICs технология смысл слов, понимание Fast Properties и Slow Properties (или режим словаря) Это не будет сложно.
На картинке ниже описано JSObject из Основная структура: при помещении члена объекта сохраняется в in-object properties из времени это время называется объектда Fast Properties режим, что означает доступ к объекту V8 будет существоватьсоответствующим из времени Воляиц Inline Cache для оптимизации после компиляции и наоборот, когда члены сохраняются; в *properties время,В это время он называется для Slow Свойства, в настоящее время с такими объектами не будут выполняться никакие операции. inline cache Оптимизированный, производительность доступа к объекту в настоящее время наихудшая (поскольку ему необходимо пройти через *properties Словари, как правило, в десятки-сотни раз медленнее, в зависимости от количества членов объекта).
Мы можем использовать %HasFastProperties, чтобы узнать, находится ли объект в режиме быстрых свойств, как показано на следующем рисунке:
delete чтобы повернуть объект slow properties Шаблон, почему? потому что delete Существует слишком много проблем, связанных с кэшированием. удалить, как показано на рисунке:
Я могу придумать четыре вышеупомянутых вопроса, просто подумав о них. Убедитесь, что они заполнены. delete Безопасность может быть слишком сложной, поэтому поддерживайте delete назадиз hidden class Очень хлопотно, V8 Возьмите путь, чтобы направить Волю in-object Отпустите его, а затем скопируйте и сохраните свойства объекта в *properties внутри этот индивидуальныйобъект больше не будет открыт в будущем. ICs оптимизация Понятно,в это времяэтотсвоего рода деградацияназадизобъект Сразусказатьдля slow properties (или режим словаря)。
7.3 использовать Hidden Class найти переполнение памяти вне (heapdump)
Hidden Class даморе академическое имя, существование V8 внутри соответствует из「названия проекта」да Map,Можетсуществовать heapdump внутри Видеть:
использовать Находить Hidden Class этот метод позволяет быстро найти большие партии одного и того же shapes изобъект О, очень удобно находить проблемы с переполнением памяти.
8.1 встроенное расширение
и ключевое слово C++ внутрииз inline одинаковое,Воляфункцияпрямо заранее Расширять,На одно время меньше на настройку функции стека для использования служебных данных домена.
8.2 Анализ побега
на основе Sea Of Nodes из PL Теоретический прогрессоптимизация,Анализ жизненного цикла объекта,еслиобъектдаодноразовыйиз,Так Сразумогу сделатькомпилировать замену для улучшения производительности,например Изображение нижевнутриобъект o Только используется а, Так может стать оптимизация вправо таким образом Таким образом, уменьшите выделение памяти объектам и увеличьте скорость адресации:
8.3 Заранее подайте заявку на внутриобъектную память для пустых объектов
проходитьбить heapdump таким образом вы можете найти вторую строку ниже из пустого объекта shallow size да 28 Байты, затем один 16 байт:
window.arr = []; // ударь один раз heapdump
arr.push({}); // ударь один раз heapdump
arr.push({ ggg: undefined });
причина:V8 Предполагается, что новый из будет установлен после пустого объекта. key поднимись, так что будет заранее malloc некоторый in-object поле для JSObject Вверх, наконец да 28, чем 16 должно быть больше, и если вот так закрепить третий ряд, то будет только; malloc один in-object Поле (собственно посмотрите на картинку внутри тоже естьодин proto поле).
Так new Object() Шерстяная ткань? То же самое; Object.create(null) Шерстяная ткань? В данном случае я не буду обращаться, мелко size На данный момент минимум 12 байт.
28 - 12 = 16 байт, а указатель один занимает 4 байты, поэтому V8 Для одного пустого объекта по умолчанию будет создано несколько объектов. 4 индивидуальный in-object поля для подготовки к последующему использованию, и такое предварительное выделение памяти,встречасуществоватьв следующий раз GC время Волябезиспользоватьприезжатьизперерабатывать,Эта технология называется 「Slack Tracking «Расслабленное отслеживание».
8.4 Другие методы оптимизации
v8 внутритакжеиметьмногие за string / Array изоптимизациятехнология,Эта технологияоптимизации в основном предполагает ICs По поводу оптимизации, я не буду писать про Расширять, пожалуйста, обратитесь по ссылке ниже (на самом деле большая часть объектоптимизациитехнологий окружена да V8 объектная модель для выполнения из).
Safari из WebKit JSCore двигатель Существует такжена основе LLVM Бэкэнд из JIT технология, поэтому множество оптимизаций означает распространенность, например safari Существует также type feedback Отслеживание атрибутов, Существует также Собственныйиз hidden class / ICs Реализовано, можно открыть safari Инструменты отладки видят среду выполнения type feedback:(macOS、iOS、iPadOS Все включено JIT,существовать chrome После оптимизации все платформы смогут получить выгоду).
существовать Этиоптимизациятехнологияиздобавлятьдержатьначальство,safari jscore В некоторых случаях это будет даже лучше, чем chrome v8 Еще быстрее:
В большинстве бизнес-сценариев больше внимания уделяется ремонтопригодности.,Производительность – не самое главное,Кроме того, написание js для логики движка/нижнего уровня может не соответствовать лучшим практикам.,Когда он будет выглядеть очень грязным,В этом разделе обобщены общие примеры отдельных людей, сталкивающихся с,Для справки:
10.1 Горячая функция
Функции точки доступа будут первыми turbofan компилироватьдлямашинный код,Производительность будет лучше,какиспользовать Хорошоиндивидуальныйхарактеристика?Воляпроектвнутрииз Некоторые высокочастотные атомарные операции разбиваются на независимыефункция,людидля Создавайте горячие точкикод,например, рассчитать расстояние до точки,Преобразование единиц измерения и т. д. Они требуют высокой производительности:
10.2 Разборка функций
удалять Понятноупоминалось Ранееиз-за пределами горячей зоны, после разборки функциональность достаточно кратковременная, Так V8 существоватьнастраиватьиспользоватьвремявстреча Делать inline Расширьте оптимизацию и сэкономьте на одном стеке вызовов.
10.3 Уменьшение состояния функции (моно)
спередииз add из примера мы можем знать, V8 TurboFan оптимизациядана основе assumption из, следует попытаться сохранить мономорфизм функций (Мономорфный), или, как говорят, уменьшает функциональное состояние, в частности не передает высокочастотную функцию. Union Types делатьдляпараметр。(этотиндивидуальный Нетдостаточно точный,большинствода Нетхотетьбитьперерывпараметриз V8 Представление внутреннего типа и сборка контрольная точка, например, числа с плавающей запятой будут передаваться за один раз, а затем SMI Даже если это так number тоже сломается v8 изгипотеза,потому чтодля v8 Внутренняя реализация чисел с плавающей запятой будет упакована в рамку, а маленькие целые числа SMI Нет, логика сборки у них разная).
Рекомендуется TypeScript писать js При использовании ограничения типа входного параметра функция может эффективно гарантировать мономорфный характер функции, легче писать высокую производительность. js код.
10.4 Сохранять порядок назначения объектов неизменным (скрытый класс)
Разный порядок присвоения приведет к разным результатам. Hidden Class цепочка, отличная от цепочки, сделать невозможно ICs оптимизация.
10.5 Лучше всего добавить одно значение по умолчанию в объявление поля класса внутри.
class A {
a?: number
}
class A {
a = undefined // или null
}
Причина та же, что и в предыдущем пункте, первый A иметь shapes цепь Пустой объект+a, и последний даопределениз a .
Да,Назначение потребует немного больше памяти,Будьте осторожны со сценариями, чувствительными к памяти.
10.6. Избегайте использования удаления
delete назадчтобы повернуть объект Slow Properties режим, в этом режиме изобъектов не будет inline cache приезжатьоптимизацияназадизкомпиляциямашинный кодвнутри,Большое влияние на производительность,кроме тогоэтот Образецизобъектеслиприезжать Чуанизразговаривать Сразувстречаприезжать Сработало в「противоположныйоптимизация」Волязагрязнение имеетоптимизация Проходитьизкод.
10.7. Избегайте деоптимизации
Предыдущий пример внутри упоминался,После обратной оптимизации производительность функции будет не лучше, чем в начале.,Другими словами「feedback Загрязнение», мы должны стараться изо всех сил избегать отражения (т.е. checkpoint был сломан из ситуации).
10.8 Статика лучше, чем динамика
Ситуация такого типа обсуждалась ранее. Статический метод записи V8 может оптимизировать микросхемы и напрямую изменить доступ к атрибутам на доступ внутри объекта, что может быть почти в сто раз быстрее, чем динамический поиск ключей.
10.9 Буквальные объявления лучше процедурных объявлений
const obj = { a: 1, b: 2 };
const obj = {};
obj.a = 1;
obj.b = 2;
от Hidden Class С точки зрения второго типа, Hidden Class Изменено три раза, и первое прямое утверждение фактически неявно. Hidden Class Да, V8 Вы можете заранее выполнить статический анализ.
10.10 Постарайтесь обеспечитьобъект Сразу Толькоделатьиспользоватьсуществоватьодинфункция Внутри(анализ побега)
10.11 Ref<T> Проблемы с производительностью
существуют React / Vue внутрииметь Эта конструкция Ref используется для доступа к одной и той же операции экземпляра (аналогично указателю).
type Ref<T> = {
ref: T
}
// React изда current делатьдля key
type ReactRef<T> = { current: T }
упоминалось ранее Проходитьиз ICs оптимизация, поэтому приведенная выше структура не приведет к серьезной потере производительности, она будет потреблять немного больше памяти, в большинстве случаев вы можете быть уверены: используйте (больше потребления 16 байт).
Особая благодарность Юаньбао за его мощную поддержку в моей работе ❤
-End-
Автор оригинала|Лай Божи