Является ли чтение свойства "длина" массива действительно такой дорогой операцией в JavaScript?
Я всегда предполагал, что кэширование длины массива в JavaScript является хорошей идеей (особенно в состоянии for
цикл) из-за дороговизны вычисления длины массива.
пример
for (var i = 0; i < arr.length; i++) { }
// vs
for (var i = 0, arrLength = arr.length; i < arrLength; i++) { }
однако я подумал, что, возможно,length
свойство обновляется только при создании и изменении массива. Поэтому чтение его не должно быть слишком дорогостоящей операцией, в отличие от чтения, хранящегося в переменной (в отличие от других методы на других языках, которые, возможно, потребуется искать в памяти, чтобы найти конец чего-то, например strlen()
В C).
у меня есть два вопроса. Я также заинтересован в том, как это работает, поэтому, пожалуйста, не бейте меня с преждевременная оптимизация палкой.
предположим, что движки JavaScript в браузерах.
- есть ли какие-либо преимущества для кэширования
length
свойство массива в JavaScript? Есть ли что-то еще, что связано с чтением локальной переменной над свойством объекта? - - это
length
свойство просто изменено при создании и наshift()
иpop()
введите методы, которые не возвращают новый массив и в противном случае просто хранятся как целое число?
6 ответов:
Ну, я бы сказал, что это дорого, но потом я написал небольшой тест @ jsperf.com и к моему удивлению используя
i<array.length
на самом деле было быстрее в Chrome, и в FF(4) это не имело значения.Я подозреваю, что длина хранится как целое число (Uint32). Из ECMA-спецификации (262 ed. 5, стр. 121):
каждый объект Array имеет свойство length, значение которого всегда неотрицательное целое число меньше 232. Значение длина собственность численно больше, чем имя каждое свойство, имя которого является массивом индекс; всякий раз, когда свойство массива объект создан или изменен, свойства настраиваются по мере необходимости чтобы сохранить этот инвариант. В частности, когда имущество добавлено чье имя является индексом массива, свойство length изменяется, если необходимо, чтобы быть одним больше, чем числовое значение этого индекса массива; и всякий раз, когда свойство length изменилось каждое свойство чье имя индекс массива, значение которого не меньше, чем новая длина автоматически удалять. Это ограничение применяется только к собственным свойствам объекта Объект массива и не влияет на длина или свойства индекса массива, которые может быть унаследован от своих прототипов
Фух! Не знаю, привыкну ли я когда-нибудь к такому языку ...
наконец, у нас всегда есть наш старый добрый отстает от браузера. В IE (9, 8, 7) кэширование длины действительно быстрее. Один из многих других причин не использовать IE, я говорю.
TL; DR:
из того, что я могу собрать, это кажется как длина массива кэшируется внутри (по крайней мере в V8)..
(подробности? Читайте дальше:))
Итак, этот вопрос несколько раз звучал в моей голове, и я решил добраться до корня проблемы (по крайней мере, в одной реализации).
копание вокруг источника V8 дало JSArray класса.
// The JSArray describes JavaScript Arrays // Such an array can be in one of two modes: // - fast, backing storage is a FixedArray and length <= elements.length(); // Please note: push and pop can be used to grow and shrink the array. // - slow, backing storage is a HashTable with numbers as keys.
Я делаю предположение, что тип элементов массива определяет, является ли он быстрым или медленным. Я дошел до немного флаг устанавливается в
set_has_fast_elements
(set_bit_field2(bit_field2() | (1 << kHasFastElements))
), где я решил, что нарисую линию копания, поскольку я искал в коде google и не имел источника локально.вот это кажется это любой времени любой операция выполняется на массиве (который является дочерним классом
JSObject
, а вызовNormalizeElements()
, которая выполняет следующее:// Compute the effective length. int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() : array->length();
Итак, отвечая на ваши вопросы:
- в Chrome (или других браузерах, использующих V8) нет никаких преимуществ для кэширования
length
свойство массива (если вы не делаете что-то странное, что заставит его бытьslow
(Я не уверен, что это за условия) - сказав это, я, скорее всего, продолжу кэшироватьlength
пока я не получу шанс пройти все реализации браузера ОС ;)- The
length
собственность, кажется, изменяется после любой работы на объекте.Edit:
на стороне Примечание, кажется, что" пустой " массив на самом деле выделяется, чтобы иметь 4 элемента:
// Number of element slots to pre-allocate for an empty array. static const int kPreallocatedArrayElements = 4;
Я не уверен, сколько элементов массив растет, как только границы были превышены - я не копал это глубокий :)
еще один набор производительности тесты. Цикл выполняется над массивом из миллиона случайных чисел с пустым циклом.
в Chrome циклы с кэшированными и некэшированными длинами часов почти одинаковы, поэтому я предполагаю, что это V8 оптимизация для кэширования длины.
в Safari и Firefox кэшированная длина была последовательно примерно в 2 раза быстрее, чем в некэшированной версии.
просто к сведению:
в некоторых браузерах (я заметил это в Safari, IE и Opera) вы можете получить повышение скорости, кэшируя длину внутри объявления цикла for:
var j; for (var i = 0, len = arr.length; i < len; i++) { j = arr[i]; }
я отредактировал тест jsperf @KooiInc выше, чтобы добавить в этом случае.
эта статья исследует автоматическое кэширование в V8 и Chrome, задавая IRHydra для генерации кода:
Как Гринч украл массива.длина доступа Вячеслав Егоров
Он обнаружил, что при определенных условиях кэширование вручную
.length
фактически добавлены накладные расходы, а не повышение производительности!но в любом случае, такого рода микро-оптимизации вряд ли удастся добиться каких-либо заметный выигрыш для ваших пользователей. Для их пользы и для вашей, вместо этого сосредоточьтесь на коде, который ясно читается, и используя хорошие структуры данных и алгоритмы в вашем коде!
избегайте преждевременной оптимизации: сосредоточьтесь на элегантном коде, пока не возникнет проблема производительности. Только тогда, искать узкое место через профилирование, а затем оптимизировать часть кодекса.