为什么以下两段代码运行时如此不同?

时间:2020-11-03 19:02:06

标签: javascript node.js google-chrome v8

看看这两段代码,第二段只添加第三行。但是时间是84倍。有人可以解释为什么吗?

foo bar

let LIMIT = 9999999;
let arr = new Array(LIMIT);
// arr.push(1);
console.time('Array insertion time');
for (let i = 1; i < LIMIT; i++) {
  arr[i] = i;
}
console.timeEnd('Array insertion time');

let LIMIT = 9999999;
let arr = new Array(LIMIT);
arr.push(1);
console.time('Array insertion time');
for (let i = 1; i < LIMIT; i++) {
  arr[i] = i;
}
console.timeEnd('Array insertion time');

1 个答案:

答案 0 :(得分:1)

arr.push(1)操作创建一个“稀疏”数组:它在索引9999999处存在一个元素。 V8将这种稀疏数组的内部表示形式切换为“字典模式”,即该数组的后备存储是一个索引→元素字典,因为与仅使用一个元素的1000万个元素分配空间相比,它的内存效率明显更高。 / p>

不利的一面是,与“快速/密集模式”下的数组相比,访问(读或写)字典模式数组的元素要慢:每次访问都必须计算正确的字典索引,并且(在这种情况下,手)字典必须增长几次,这意味着将所有现有元素复制到新的后备存储中。

随着数组被填满,V8注意到它变得越来越密集,并在某些时候将其转换回“快速/密集模式”。到那时,已经观察到大多数放缓。循环的其余部分也增加了成本,因为此时arr[i] = i;存储区已经看到了两种类型的数组(字典模式和密集模式),因此在每次迭代中,它必须检测到数组的状态现在就可以进行处理,这(不出所料)比不必做出决定要花费更多的时间。

一般性结论:由于JavaScript既具有动态性又具有灵活性,因此对于外观非常相似的代码,引擎的行为可能会大不相同。例如,由于引擎针对一种情况优化了内存消耗,另一种针对执行速度进行了优化,或者因为其中一种情况允许引擎使用某些不适用于另一种情况的快捷方式(无论出于何种原因)。好消息是,在许多情况下,正确且可理解/直观/简单的代码也往往会运行得很好(在此示例中,流浪arr.push看起来很像个错误)。