logo
/
JavaScriptのreduceによるオブジェクト構築はめちゃめちゃ遅くなる
2023-01-13
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

多分そう オブジェクトが大きいほど重くなる気がする?