Является ли чтение свойства "длина" массива действительно такой дорогой операцией в 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 в браузерах.

  1. есть ли какие-либо преимущества для кэширования length свойство массива в JavaScript? Есть ли что-то еще, что связано с чтением локальной переменной над свойством объекта?
  2. - это length свойство просто изменено при создании и на shift() и pop() введите методы, которые не возвращают новый массив и в противном случае просто хранятся как целое число?
6 59

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();

Итак, отвечая на ваши вопросы:

  1. в Chrome (или других браузерах, использующих V8) нет никаких преимуществ для кэширования length свойство массива (если вы не делаете что-то странное, что заставит его быть slow (Я не уверен, что это за условия) - сказав это, я, скорее всего, продолжу кэшировать length пока я не получу шанс пройти все реализации браузера ОС ;)
  2. 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 фактически добавлены накладные расходы, а не повышение производительности!

но в любом случае, такого рода микро-оптимизации вряд ли удастся добиться каких-либо заметный выигрыш для ваших пользователей. Для их пользы и для вашей, вместо этого сосредоточьтесь на коде, который ясно читается, и используя хорошие структуры данных и алгоритмы в вашем коде!

избегайте преждевременной оптимизации: сосредоточьтесь на элегантном коде, пока не возникнет проблема производительности. Только тогда, искать узкое место через профилирование, а затем оптимизировать часть кодекса.

Не думайте, что это верно для всех итерационных коллекций. Например, кэширование длина HTMLCollection на 65% быстрее в Chrome (версия 41) и на 35% быстрее в Firefox (версия 36).

http://jsperf.com/array-length-in-loop-dom