Javascript Looping Deathmatch

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

Я решил провести тесты сам, используя набор данных из 16,105,100 чисел; достаточно длинный набор, чтобы выявить любые различия в алгоритме цикла.

Испытания следующие

Стек: Узел 9.8.0, работающий на MacBook Pro

map.js

const data = require(‘./data’)
const results = data.map(i => i * i)

forLoop.js

const data = require(‘./data’)
const results = []
for (let i = 0; i < data.length; i++) {
  results.push(data[i] * data[i])
}

forInLoop.js

const data = require(‘./data’)
const results = []
for (const i in data) {
  results.push(data[i] * data[i])
}

forOfLoop.js

const data = require(‘./data’)
const results = []
for (const i of data) {
  results.push(i * i)
}

forEach.js

const data = require(‘./data’)
const results = []
data.forEach(i => {
  results.push(i * i)
})

while.js

const data = require('./data')
const results = []
let i = 0
while (i < data.length) {
  results.push(data[i] * data[i])
  i++
}

Результаты

Я рассчитал время каждого подхода, используя стандартную функцию unix time.

time node forLoop

real 0m1.693s
user 0m1.413s
sys  0m0.298s

time node forInLoop

real 0m5.602s
user 0m5.348s
sys  0m0.642s

time node forOfLoop

real 0m1.740s
user 0m1.463s
sys  0m0.297s

time node forEach

real 0m1.863s
user 0m1.584s
sys  0m0.291s

time node map

real 0m3.330s
user 0m3.137s
sys  0m0.202s

time node while

real 0m1.959s
user 0m1.516s
sys  0m0.417s

Совершенно очевидно, что классический for цикл - самый быстрый, за ним следует цикл стиля for..of, затем forEach цикл, затем while.

Петли map занимают 5-е место, а петли стиля for in - худшие с большим отрывом.

В любом случае никто в здравом уме не стал бы использовать цикл for..in для цикла по массиву, поскольку for..in предназначен для использования в цикле по свойствам объекта. В идеале вы не должны использовать for..of, поскольку он предназначен для циклического перебора значений свойств объекта. Это действительно случайно, что, как работают массивы Javascript, индекс считается свойством, а значение - соответствующим значением.

Ни for..in, ни for..of также не гарантируют, что они будут зацикливаться в порядке свойств объекта, что делает их непригодными для чего-либо, где вам нужен детерминированный порядок.

Интересно, что forEach намного быстрее, чем map, особенно с учетом того, сколько карт используется в современном коде javascript, и оба они работают, вызывая анонимную функцию для значений.

Также интересно, что forIn использует гораздо больше системного времени, чем другие.

Выводы

Используйте классический for цикл для максимальной общей скорости или forEach цикл, если вы предпочитаете более functional стиль кода.

Вы можете использовать forOf, если вас не волнует порядок выполнения.

Array.prototype.map работает медленно, а while циклы подробны и не дают реальных преимуществ в производительности.

Нравится, но не подписчик? Вы можете поддержать автора, присоединившись через davesag.medium.com.