Было бы очень интересно задокументировать производительность, связанную с массивами и объектами в JavaScript (особенно Google V8). Я нигде в Интернете не нашел исчерпывающей статьи по этой теме.
Я понимаю, что некоторые объекты используют классы в качестве базовой структуры данных. Если свойств много, иногда это рассматривается как хеш-таблица?
Я также понимаю, что с массивами иногда обращаются как с массивами C ++ (т.е. быстрая случайная индексация, медленное удаление и изменение размера). А в других случаях они больше похожи на объекты (быстрая индексация, быстрая вставка / удаление, больше памяти). И, возможно, иногда они хранятся в виде связанных списков (т.е. медленная случайная индексация, быстрое удаление / вставка в начале / конце)
Какова точная производительность извлечения массивов / объектов и манипуляций с ними в JavaScript? (специально для Google V8)
В частности, каково влияние на производительность:
- Добавление свойства к объекту
- Удаление свойства из объекта
- Индексирование свойства в объекте
- Добавление элемента в массив
- Удаление элемента из массива
- Индексирование элемента в массиве
- Вызов Array.pop ()
- Вызов Array.push ()
- Вызов Array.shift ()
- Вызов Array.unshift ()
- Вызов Array.slice ()
Также приветствуются любые статьи или ссылки для получения более подробной информации. :)
EDIT: мне действительно интересно, как массивы и объекты JavaScript работают под капотом. Кроме того, в каком контексте движок V8 «знает» о «переключении» на другую структуру данных?
Например, предположим, что я создаю массив с ...
var arr = [];
arr[10000000] = 20;
arr.push(21);
Что здесь на самом деле происходит?
Или ... что с этим ... ???
var arr = [];
//Add lots of items
for(var i = 0; i < 1000000; i++)
arr[i] = Math.random();
//Now I use it like a queue...
for(var i = 0; i < arr.length; i++)
{
var item = arr[i].shift();
//Do something with item...
}
Для обычных массивов производительность была бы ужасной; тогда как, если использовался LinkedList ... не так уж и плохо.
источник
Ответы:
Я создал набор тестов именно для изучения этих (и других) проблем ( архивная копия ).
И в этом смысле вы можете увидеть проблемы с производительностью в этом тестере из более чем 50 тестовых случаев (это займет много времени).
Кроме того, как следует из названия, в нем исследуется использование природы связанного списка структуры DOM.
(В настоящее время не работает, перестраивается) Подробнее об этом в моем блоге .
Резюме следующее
Array.shift()
быстро ~ примерно в 6 раз медленнее, чем извлечение массива, но примерно в 100 раз быстрее, чем удаление атрибута объекта.Array.push( data );
это быстрее, чемArray[nextIndex] = data
почти в 20 (динамический массив) в 10 (фиксированный массив) раз.Array.unshift(data)
работает медленнее, чем ожидалось, и примерно в 5 раз медленнее, чем добавление нового свойства.array[index] = null
происходит быстрее, чем его удалениеdelete array[index]
(undefined) в массиве примерно на 4 ++ быстрее.obj[attr] = null
примерно в 2 раза медленнее, чем просто удаление атрибута.delete obj[attr]
Array.splice(index,0,data)
медленный, очень медленный.Array.splice(index,1,data)
но оптимизирован (без изменения длины) и в 100 раз быстрее, чем просто сращиваниеArray.splice(index,0,data)
dll.splice(index,1)
удаления (где он сломал тестовую систему).Примечание. Эти показатели применимы только к большим массивам / объектам, которые в версии 8 не «полностью оптимизированы». Могут быть очень изолированные случаи оптимальной производительности для размера массива / объекта меньше произвольного размера (24?). Более подробную информацию можно найти в нескольких видеороликах Google IO.
Примечание 2: эти замечательные результаты производительности не используются в разных браузерах, особенно в
*cough*
IE. Также тест огромен, поэтому мне еще предстоит полностью проанализировать и оценить результаты: отредактируйте его в =)Обновленное примечание (декабрь 2012 г.): у представителей Google есть видеоролики на YouTube, в которых описывается внутренняя работа самого Chrome (например, когда он переключается с массива связанных списков на фиксированный массив и т. Д.), А также способы их оптимизации. См. GDC 2012: От консоли к Chrome для получения дополнительной информации.
источник
На базовом уровне, который остается в сфере JavaScript, свойства объектов представляют собой гораздо более сложные сущности. Вы можете создавать свойства с помощью сеттеров / получателей с различной перечисляемостью, возможностью записи и настройки. Элемент в массиве не может быть настроен таким образом: он либо существует, либо нет. На базовом уровне движка это позволяет намного больше оптимизировать с точки зрения организации памяти, которая представляет структуру.
Что касается идентификации массива из объекта (словаря), JS-движки всегда делали явные строки между ними. Вот почему существует множество статей о методах создания полу-фальшивого объекта, подобного массиву, который ведет себя как один, но допускает другие функции. Причина, по которой такое разделение существует, заключается в том, что сами движки JS хранят их по-разному.
Свойства могут быть сохранены в объекте массива, но это просто демонстрирует, как JavaScript настаивает на превращении всего объекта в объект. Индексированные значения в массиве хранятся иначе, чем любые свойства, которые вы решите установить для объекта массива, представляющего базовые данные массива.
Всякий раз, когда вы используете законный объект массива и используете один из стандартных методов манипулирования этим массивом, вы будете обращаться к базовым данным массива. В частности, в V8 они по сути такие же, как массив C ++, поэтому будут применяться эти правила. Если по какой-то причине вы работаете с массивом, который движок не может с уверенностью определить как массив, то ваша почва гораздо шатче. Однако с последними версиями V8 есть больше возможностей для работы. Например, можно создать класс, прототипом которого является Array.prototype, и при этом получить эффективный доступ к различным методам работы с собственными массивами. Но это недавнее изменение.
Здесь могут пригодиться конкретные ссылки на недавние изменения в работе с массивами:
В качестве дополнительной информации, здесь Array Pop и Array Push непосредственно из источника V8, оба реализованы в самом JS:
источник
Я хотел бы дополнить существующие ответы исследованием вопроса о том, как реализации ведут себя в отношении растущих массивов: если они реализуют их «обычным» способом, можно будет увидеть много быстрых нажатий с редкими, чередующимися медленными нажатиями, в этот момент реализация копирует внутреннее представление массива от одного буфера к большему.
Вы можете очень хорошо увидеть этот эффект, это из Chrome:
Несмотря на то, что каждое нажатие профилировано, выходные данные содержат только те, для которых время превышает определенный порог. Для каждого теста я настроил порог, чтобы исключить все толчки, которые, кажется, представляют собой быстрые толчки.
Таким образом, первое число представляет, какой элемент был вставлен (первая строка - для 17-го элемента), второе - сколько времени это заняло (для многих массивов эталонный тест выполняется параллельно), а последнее значение - это деление элемента первый номер по номеру в предыдущей строке.
Все строки с временем выполнения менее 2 мс исключаются для Chrome.
Вы можете видеть, что Chrome увеличивает размер массива в 1,5 раза, плюс некоторое смещение для учета небольших массивов.
Для Firefox это степень двойки:
Мне пришлось немного поднять порог в Firefox, поэтому мы начинаем с # 126.
С IE мы получаем смесь:
Сначала это степень двойки, а затем она переходит в степень пяти третей.
Итак, все распространенные реализации используют «нормальный» способ работы с массивами (например, вместо того, чтобы сходить с ума по веревкам ).
Вот код теста, а вот скрипка, в которой он находится.
источник
При работе под node.js 0.10 (построенной на v8) я наблюдал использование ЦП, которое казалось чрезмерным для рабочей нагрузки. Я проследил одну проблему производительности до функции, которая проверяла наличие строки в массиве. Итак, я провел несколько тестов.
Загрузка 91 тыс. Записей в массив (с помощью проверки и нажатия) выполняется быстрее, чем установка obj [key] = value.
В следующем тесте я просмотрел каждое имя хоста в списке один раз (91 тыс. Итераций, чтобы усреднить время поиска):
Здесь используется приложение Haraka (SMTP-сервер), которое загружает host_list один раз при запуске (и после изменений), а затем выполняет этот поиск миллионы раз во время работы. Переключение на объект было огромным выигрышем в производительности.
источник