【问题标题】:Why is for-of loop faster than standard for loop for small arrays and slower for larger arrays?为什么 for-of 循​​环比小型数组的标准 for 循环快,而大型数组的循环慢?
【发布时间】:2019-03-26 02:22:24
【问题描述】:

在 JavaScript 中,我注意到 ES6 for ... of 循环的性能与传统的 for (start; stop; step) 循环有很大不同。

基准测试

const n = 10000;
const arr = Array(n).fill().map((e, i) => i); // [0, n)

console.log('n =', n);

let sum1 = 0;
console.time('for let i');
for (let i = 0; i < arr.length; i++) {
  sum1 += arr[i];
}
console.timeEnd('for let i');

let sum2 = 0;
console.time('for of');
for (let v of arr) {
  sum2 += v;
}
console.timeEnd('for of');

结果

n = 10
for let i: 0.350ms
for of: 0.015ms
-----
n = 100
for let i: 0.354ms
for of: 0.023ms
-----
n = 1000
for let i: 0.429ms
for of: 0.111ms
-----
n = 10000
for let i: 1.048ms
for of: 2.138ms
-----
n = 100000
for let i: 9.452ms
for of: 13.644ms

(使用 Node.js v10.11.0 测试)

如您所见,随着 n 的增加,for-of 循​​环的速度以比标准 for 循环更快的速度下降。为什么对于较小的数组,for-of 循​​环会更快,而对于较大的数组,for-of 循​​环会更慢?

【问题讨论】:

  • 你的时间很可能是错的。
  • @Veedrac repl.it/@BrendenCampbell/ForOfPerfTest 考虑到 repl.it 上的可变 CPU 可用性,在大多数测试中,我的结果是可重复的。
  • 尝试切换哪个循环是第一个。如果你的时间是真实的,这不会改变任何事情。
  • "ES6 for ... of loop 的性能与传统的 for (start; stop; step) 循环有很大不同" - 在标准阵列上,they should perform exactly the same。请将其报告为错误。
  • @Veedrac 你是对的。切换顺序后,我看到完全相反。第一个循环(for-of)看起来更慢

标签: javascript arrays performance


【解决方案1】:

当对较小的值进行基准测试时,开销操作会对测试产生更大的影响。

例如,如果变量初始化和内存分配需要 0.1 毫秒,这在 n > 1000 时可以忽略不计,但在 n = 10 时很重要。

在这种情况下,for/of 运算符允许 V8 引擎优化循环操作(减少上述开销)。例如,它可以将数组项预加载到堆栈或类似内容中。

for/let 操作将独立于整个数组处理每个项目,并且在变量使用方面更加明确(减少引擎可以做的优化量)。

【讨论】:

  • 如果您只是交换基准代码中的for letfor of 块并使用n=10n=100 执行它呢?你能解释一下为什么时间会发生如此巨大的变化以及为什么现在for let 更快吗? :)
【解决方案2】:

我建议看一下 microbenchmarking 的概念,并熟悉 this great answer 以在 JS 中进行微基准测试。

简而言之,在测试诸如 for 循环之类的小事情时,您很容易让您的测试逻辑与 other ongoing processes under the hood 发生干扰。例如,如果您交换测试用例中的执行顺序,使 for offor let 之前执行,您可能会注意到较小的 n 值的令人惊讶的时间变化(剧透:在这种情况下 for letn=10n=100 的比赛中获胜

因此,您的原因问题的答案:for let 在您的列表中较慢,因为它更接近程序启动并在更“冷”的 vm 上执行,也为随后的 for let 语句预热。 n 越大,这种副作用对测量的执行时间的影响就越小。

这就是为什么微基准测试意味着执行一系列相同的测试而不是仅仅执行一个测试 - 以使较小的副作用在更高的规模上不那么显着

另外,请注意,衡量语句性能的规范单位是“每单位时间的总操作数”,而不是每组操作的绝对时间

可以在here找到您主题的示例微基准测试

【讨论】:

    【解决方案3】:

    for-of 循​​环data 上(就像数组的单元格),而不是对象本身。大大提高性能。

    其他循环遍历对象。 for-of 循​​环使 internal 代码更简单。

    而不是访问数组的内存地址(一个十六进制数字),这对于 n 大小的较大数组需要更长的时间。这些类型的循环更适合少量。

    for-of 循​​环遍历数字 数据 而不是对象,这在遍历大量元素时效率更高。您将必要的循环类型应用于您的代码的具体情况。

    这就像一匹马和一辆汽车。这匹马适合不到 100 英里的旅程,而不是 100 多英里的旅程。当汽车可以做得更好时。


    有关更多信息,请参阅此链接。 - https://hacks.mozilla.org/2015/04/es6-in-depth-iterators-and-the-for-of-loop/

    【讨论】:

      【解决方案4】:

      让我来解释一下,在这种情况下 forfor...of 更快,因为操作要简单得多,详细解释可以找到here:

      for(var VariableDeclarationList; Expression; Expression) Statement

      for (let i = 0; i < arr.length; i++) {
        sum1 += arr[i];
      }
      

      所以这里发生了什么:

      • 检查 i
      • 我加一

      但事情有点不同对于...的

      for(var ForBinding in Expression) Statement

      let sum2 = 0;
      console.time('for of');
      for (let v of arr) {
        sum2 += v;
      }
      

      据我所知,for..of 使用特定于对象的迭代器并循环由此生成的值,因此这将比 for 花费更多时间,所以事情这里有点意思。如果您想了解更多信息,请检查运行时语义的差异:

      用于运行时语义:here

      for...of 运行时语义:here

      这种情况仅是有效的用户定义的 Symbol.iterators,但内置的迭代器应该被优化以在 for...of 而不是 for 中更好地工作> “感谢@Aryter

      那么为什么您的测试不准确?因为您在 for 循环之后生成 for...of 所以V8 engine 正在那里进行一些优化。

      我对优化的假设与 JavaScript 属性有关,并且当对象很小时会产生显着的速度差异 V8 引擎,更详细的可以在这里找到V8 Engine fast properties

      希望现在更加清晰......

      【讨论】:

      • 如果您投反对票,请留下您的评论和理由,以便我也可以学习。
      • 也许反对票是因为您以 for is always faster than for...of 开头,而 OP 中的基准测试表明这不是真的。
      • 我进行了编辑,并说“在这种情况下”希望更清楚。我还解释了为什么基准测试不正确。
      • 不仅如此,您实际上并没有解释任何事情。 OP 很可能想知道为什么。我希望看到代码的分析器运行,而不是“嗯,因为它是!”个人对赏金问题的解释,这就是我对你投反对票的原因。
      • 你忘了说for 也必须做arr[i]。您的声明对于用户定义的Symbol.iterators 是正确的,但是应该优化内置迭代器以在for...of 而不是for 中更好地工作。如果我们只讨论语义,for 每次都重新绑定i 并访问arr[i]for...of 每次都重新绑定v,并在内部访问arr[i],但从来没有那么简单,所以谈论动作的“简单性”并不总是与速度相关。
      猜你喜欢
      • 1970-01-01
      • 2021-10-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-30
      • 2020-07-10
      • 2018-05-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多