Способы обхода массива в JavaScript и скорость их работы



В JavaScrip'те есть много способов работы с массивами: for in, for of, array.map, array.forEach, array.reduce, array.filter. И хоть каждый из них имеет своё специфическое предназначение, часто они используются бездумно.

Я уже год собеседую js программистов, и у меня есть 1 простейшая задачка: законсольлогить все элементы массива. В как минимум половине случаев начинают юзать array.map, затем вспоминают про array.forEach и менее половины кандидатов знают про уже давно стандартные for of/for in.

Для любого нормального программиста понятно, что array.map/array.forEach работают медленнее чем for of/for in, потому что на каждую операцию над элементом массива вызывается функция, со всеми своими накладными расходами. Но я задумался, а насколько именно отличается производительность функция обхода массива?

Давайте напишем небольшой тестик:

const rounds = 1000;
let testArray = [];

for(let i=0; i<1000000; i++)
  testArray.push(1);

let res = 0;
let start0 = new Date();
for(let i=0; i<rounds; i++)
  for(let index in testArray) {

  }
let end0 = new Date();

console.log('TEST0 for in', (end0 - start0)/1000);

res = 0;
let start1 = new Date();
for(let i=0; i<rounds; i++)
  for(let item of testArray) {

  }
let end1 = new Date();

console.log('TEST1 for of', (end1 - start1)/1000);

res = 0;
let start2 = new Date();
for(let i=0; i<rounds; i++)
  for(let index=0; index<testArray.length; index++) {

  }
let end2 = new Date();

console.log('TEST2 classic for', (end2 - start2)/1000);

res = 0;
let start3 = new Date();
for(let i=0; i<rounds; i++)
  testArray.forEach(function(item) {
    
  });
let end3 = new Date();

console.log('TEST3 array.forEach', (end3 - start3)/1000);

res = 0;
let start4 = new Date();
for(let i=0; i<100; i++)
  testArray.map(function(item, index, array) {
    
  });
let end4 = new Date();

console.log('TEST4 array.map', (end4 - start4)/1000);

res = 0;
let start5 = new Date();
for(let i=0; i< rounds; i++)
  testArray.reduce(function(accumulator, item) {
    
  });
let end5 = new Date();

console.log('TEST5 array.reduce', (end5 - start5)/1000);

Запускаем и через пару минут получаем результаты:

$ node test2.js 
TEST0 for in 130.6
TEST1 for of 3.053
TEST2 classic for 0.748
TEST3 array.forEach 12.883
TEST4 array.map 14.2
TEST5 array.reduce 13.552

Самый быстрый- это классический счётчик с инкрементом, за ним идёт for of.

На порядок медленнее оказались олдскульные array.forEach/array.map/array.reduce. И хоть результаты у них схожие, не забываем, что array.map возвращает массив, и под это ещё и память лишняя юзается.

Наиболее медленным оказался for in, причём медленнее на порядок. Я даже задумался протестить, не будет ли быстрее обходить ключи объекта через Object.keys а не через for in. Для этого опять напишем тестик:

const rounds = 100;
let testObj = {};

for(let i=0; i<1000000; i++)
  testObj[ `a${i}` ] = 1;

let start0 = new Date();
for(let i=0; i<rounds; i++)
  for(let index in testObj) {

  }

let end0 = new Date();

console.log('TEST0 for in', (end0 - start0)/1000);

let start1 = new Date();
for(let i=0; i<rounds; i++) {
  let keys = Object.keys(testObj)
  for(let index=0; index<keys.length; index ++) {

  }
}

let end1 = new Date();

console.log('TEST1 for in', (end1 - start1)/1000);

И результат:

$ node test2.js 
TEST0 for in 43.046
TEST1 for in 37.787

Итого, Object.keys(testObj) + for, на 12% быстрее for in. Скорее всего под капотом там происходит что-то похожее, только вместо классического for+ инкремент используется что-то типа array.forEach который и даёт 14 секунд отставания.


Сообщество: AWS

Комментариев(2)


ID: #507   Создан:
Автор: 2020

TEST0 for in 264.588 VM78:26 TEST1 for of 1.353 VM78:36 TEST2 classic for 0.818 VM78:46 TEST3 array.forEach 21.524 VM78:56 TEST4 array.map 2.181 VM78:66 TEST5 array.reduce 22.217

ID: #714   Создан:
Автор: ololoev

Сам себе шлю привет из 2021, всё что тут написал, именно для ноды, уже не совсем актуально. Она, зараза, научилась в хитрые оптимизации, и классический фор уже может рабоать +- так же как форич. Единственно, не всегда предугадаешь, когда оптимизация сработает, а когда нет ...

Всего: 2 комментариев на 1 страницах

Ваш комментарий будет анонимным. Чтобы оставить не анонимный комментарий, пожалуйста, зарегистрируйтесь



Сообщества