Reduce 性能陷阱

389 阅读1分钟

先说结论

reduce 循环体中使用 concat 方法或 ... 拓展符会导致严重的性能问题

测试数据

const data = Array(100000).fill(null).map((_, idx) => ({ num: Math.random(), idx }))

性能对比

测试数据由同一设备(M1 Macbook Air)使用 console.time 方法得出,同一功能不同实现性能差异最高可达数万倍

reduce + push

// 2.96ms
result = data.reduce((rst, cur) => {
  if (cur.num > 0.5) rst.push(cur.idx)
  return rst
}, [])

filter + map

// 4.46ms
result = data.filter(i => i.num > 0.5).map(i => i.idx)

reduce + concat

// 4746.03ms
result = data.reduce((rst, cur) => cur.num > 0.5 ? rst.concat(cur.idx) : rst, [])

reduce + ...

// 24900.75ms
result = data.reduce((rst, cur) => cur.num > 0.5 ? [...rst, cur.idx] : rst, [])

原理分析

1df44e60-21e7-4566-8b8d-e598ce787860.jpeg

通过 Performance 面板可以看到,虽然内存占用没有增长到极大值,但是出现了超高频的 GC,那么导致问题的原因也就很清晰了,每次循环中调用 concat 等 API 会不断地创建新的对象,高频的对象创建导致高频的 GC 触发,而在 GC 执行过程中,浏览器还将无法处理其他事情,比如响应用户的操作,渲染动画,从而造成页面的卡顿

文章推荐