Array.reduceは要素が多くなればなるほど動作が重くなる。
Playgroundでの実験では、5000個の配列をオブジェクトに変換する場合以下の結果となった。
const arr = [...Array(5000).keys()]
const func1 = () => {
const start = performance.now()
const obj = arr.reduce((prev, cur) => { return { ...prev, [cur]: cur } }, {})
const end = performance.now()
console.log('reduce', end - start)
}
const func2 = () => {
const start = performance.now()
const obj: Record<string, number> = {}
arr.forEach((prev, cur) => { obj[cur] = cur }, {})
const end = performance.now()
console.log('foreach', end - start)
}
func1() // `reduce` : 1389.3999999761581 ms
func2() // `foreach` : 0.10000002384185791 ms
ここまで差が出るとは思ってなかった。
商用プロダクトでforeachからreduceに変更したところパフォーマンス問題を起こしてしまったので気をつけたい。
ちなみに、reduceが遅いというわけではない。
const func3 = () => {
const start = performance.now()
const obj = arr.reduce((prev, cur) => { return prev + cur }, 0)
const end = performance.now()
console.log('reduce - acc', end - start)
}
func3() // "reduce - acc", 0.19999992847442627 ms
reduceによって大量のオブジェクトが構築(という表現が正しいかわからないが)されるのが遅い原因に思える
loopを繰り返すにあたってオブジェクトの状態は以下の様に変化する。
acc1 => {1:1}
acc2 => {1:1, 2:2}
...
acc5000 => {1:1, 2:2, ..., 5000:500}
foreachの場合、同じオブジェクトに対してプロパティを追加する。
reduceの場合、毎回スプレッドで展開して新しいオブジェクトを作成する。
そのコストが高い?もしくは作成されたオブジェクトがreduce完了まで開放されない?
reduceがいけないんじゃなくてspread(...)
がいけないのでは?
2023-01-22 追記
const NUM = 50000
const func1 = () => {
let obj = {}
for (let i = 0; i < NUM; i++) {
obj = {
...obj,
i: i
}
}
return obj
}
const func2 = () => {
const obj = {}
for (let i = 0; i < NUM; i++) {
obj[i] = i
}
return obj
}
const func3 = (fun) => {
const start = performance.now()
fun()
const end = performance.now()
console.log(end - start)
}
console.log('func0')
func3(func1) // 1.8
console.log('func1')
func3(func2) // 0.6
多分そう オブジェクトが大きいほど重くなる気がする?