在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循环对于较小的数组更快,而对于较大的数组则慢?
答案 0 :(得分:7)
当对较小的值进行基准测试时,开销操作可能会对测试产生更大的影响。
例如,如果变量初始化和内存分配花费0.1毫秒,则在n> 1000时可以忽略不计,但在n = 10时有效。
在这种情况下,for / of运算符允许V8引擎优化循环操作(减少上述开销)。例如,它可以将数组项预加载到堆栈或类似对象上。
for / let操作将独立于整个数组处理每个项目,并且在变量用法上更加明确(减少引擎可以执行的优化量)。
答案 1 :(得分:5)
我建议您研究一下microbenchmarking的概念,并熟悉this great answer在JS中的微基准测试。
简而言之,在测试诸如for循环之类的小东西时,您很容易使测试逻辑受到other ongoing processes under the hood的干扰。例如,如果在测试用例中交换执行顺序,以使for of
在for let
之前执行,您可能会注意到较小的n
值的时间变化令人惊讶( spoiler:在这种情况下,for let
赢得了n=10
和n=100
)的比赛
因此,您的“为什么”问题的答案:for let
在列表中的速度较慢,因为它离程序启动更近,并且在更多的“冷”虚拟机上执行,也为随后的{{1 }}语句。 for let
越大,这种副作用对测得的执行时间的贡献就越小。
这就是为什么微基准测试意味着要执行一系列相同的测试,而不是仅仅执行一个测试-在较小的规模上产生小的副作用就不那么重要了
此外,请注意,语句性能的标准度量单位是“每单位时间的总操作数”,而不是每组操作的绝对时间
可以找到您的主题的示例微基准测试here
答案 2 :(得分:3)
for-of仅在数据(像数组的单元格)上循环 ,而不是对象本身。大大提高了性能。
另一个循环遍历对象。 for-of循环使内部代码更简单。
而不是访问数组的内存地址(十六进制数字),对于n大小的较大数组,这需要更长的时间。这些类型的循环更适合少量。
for-of循环迭代数字 data 而不是对象,这在循环大量元素时效率更高。您将必要的循环类型应用于代码的特定情况。
这就像一匹马和一辆汽车。这匹马适合少于100英里而不是100英里以上的旅程。当汽车可以做得更好时。
有关更多信息,请参考此链接。 -https://hacks.mozilla.org/2015/04/es6-in-depth-iterators-and-the-for-of-loop/
答案 3 :(得分:0)
让我尝试解释一下,在这种情况下 for 比 for ... of 快,因为操作简单得多,详细说明可以找到here:
(var VariableDeclarationList ; Expression ; Expression )声明 >
for (let i = 0; i < arr.length; i++) {
sum1 += arr[i];
}
那么这是怎么回事:
但是的
有些不同(表达式中的 ForBinding )声明
let sum2 = 0;
console.time('for of');
for (let v of arr) {
sum2 += v;
}
据我所知, for..of 使用特定于对象的迭代器并循环其生成的值,因此这将比 for 花费更多时间。这里没什么有趣的。如果您想了解更多有关此内容的信息,请检查运行时语义上的差异:
用于运行时语义:here
用于...的运行时语义:here
这种情况仅是有效的用户定义的Symbol.iterators,但应优化内置迭代器以使其在 for ... of (而不是 for )中更好地工作>“ 感谢@Aryter ”
那么您的测试为什么不准确?因为您在 for 循环后生成了 for ... of ,所以V8 engine正在此处进行一些优化。
我认为优化与JavaScript属性有关,并且在对象较小时会产生明显的速度差异V8引擎,有关详细信息,请参见V8 Engine fast properties
希望现在更加清晰了...