首页 > 解决方案 > 如何解释 js-array 循环中无法执行的性能?

问题描述

例如,假设我们有一个包含 200 000 个元素的数组......现在我们想以不同的方式迭代它并检查最快的那个。我听说如果我们在循环之前将 array.length 保存在变量中,我们将减少执行时间,所以我尝试了下面的代码

let sum = 0
for (let i = 0; i < arr.length; ++i) sum += arr[i]

反对

let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) sum += arr[i]

但是我得到了相同的结果,就好像在这两种情况下,js 一开始只读取一次长度值。

然后我决定检查,如果在循环期间我们将更改一个数组,删除最后一个元素。

let sum = 0
for (let i = 0; i < arr.length; ++i) {
  sum += arr[i]
  if (i === 100) arr.pop()
}

反对

let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) {
  sum += arr[i]
  if (i === 100) arr.pop()
}

所以我希望第二种情况现在应该更快地工作,因为在第一种情况下,js 不可避免地应该每次检查 array.length,我很惊讶它的工作速度不是更快,而是更慢 - 从 10% 到 15%。对我来说这是无法解释的。有任何想法吗?

测试:https ://jsbench.me/tfkefwjuw2

标签: javascriptarraysperformance

解决方案


问题是

let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) {
  sum += arr[i]
  if (i === 100) arr.pop()
}

现在是不正确的,因为它循环超出了数组的末尾。终会sum如此NaN。正确的解决方案将l = arr.length - 1不会发生这种情况。

现在为什么它变得这么慢?因为只有当元素存在时,javascript 中的数组访问才快速(编译为带有指针寻址的快速路径)。当您错过时,代码将被取消优化。由于 JSbench 多次运行相同的代码,因此即使去优化仅发生在 200000 次迭代中的最后一次,后续运行也会慢得多。

有关详细信息,请参阅此演讲,其中甚至在一张幻灯片上明确说明了“避免越界读取” 。一般来说,不要使用非惯用代码来提高性能i < arr.length是惯用的,JS引擎会很好地优化它。


推荐阅读