数组调整大小性能,设置长度属性与重复推送

时间:2018-11-04 23:49:33

标签: javascript arrays performance v8

因此,我正在对以下代码进行基准测试,试图找出哪种性能更好:

varbinary(255)

结果让我有些惊讶:

'use strict';

function addSetToArrayA(array, set) {
  for (const v of set) {
    array.push(v);
  }
}
function addSetToArrayB(array, set) {
  const origLength = array.length;
  const newLength = array.length + set.size;
  array.length = newLength;
  array[newLength - 1] = 0;
  let i = origLength;
  for (const v of set) {
    array[i++] = v;
  }
}

const set = new Set([1, 2, 3, 4, 5, 6]);

console.time('addSetToArrayA');
for (let i = 0;i<0xffffff;++i) {
  const base = [1, 2, 3, 4, 5, 6];
  addSetToArrayA(base, set);
}
console.timeEnd('addSetToArrayA');

console.time('addSetToArrayB');
for (let i = 0;i<0xffffff;++i) {
  const base = [1, 2, 3, 4, 5, 6];
  addSetToArrayB(base, set);
}
console.timeEnd('addSetToArrayB');

所以我做了另一个基准测试:

addSetToArrayA: 728.773ms
addSetToArrayB: 3296.437ms

结果与我之前的经验相符:

'use strict';

const iters = 0xfffff;

console.time('32 push');
for (let i = 0;i<iters;++i) {
  const base = [1, 2, 3, 4, 5, 6];
  for (let k = 0;k<32;++k) {
    base.push(undefined);
  }
}
console.timeEnd('32 push');

console.time('32 length');
for (let i = 0;i<iters;++i) {
  const base = [1, 2, 3, 4, 5, 6];
  base.length = 32;
}
console.timeEnd('32 length');

console.time('64 push');
for (let i = 0;i<iters;++i) {
  const base = [1, 2, 3, 4, 5, 6];
  for (let k = 0;k<64;++k) {
    base.push(undefined);
  }
}
console.timeEnd('64 push');

console.time('64 length');
for (let i = 0;i<iters;++i) {
  const base = [1, 2, 3, 4, 5, 6];
  base.length = 64;
}
console.timeEnd('64 length');

console.time('128 push');
for (let i = 0;i<iters;++i) {
  const base = [1, 2, 3, 4, 5, 6];
  for (let k = 0;k<128;++k) {
    base.push(undefined);
  }
}
console.timeEnd('128 push');

console.time('128 length');
for (let i = 0;i<iters;++i) {
  const base = [1, 2, 3, 4, 5, 6];
  base.length = 128;
}
console.timeEnd('128 length');

例如。对于相对较小的阵列,通过32 push: 132.061ms 32 length: 180.745ms 64 push: 284.575ms 64 length: 212.465ms 128 push: 586.747ms 128 length: 268.689ms 重新调整大小比重复使用.length慢,而对于较大的阵列则更快(如预期)。

使用.push().length = ...时,V8是否执行不同类型的数组大小调整? 这与V8处理稀疏数组有关吗?

在Node@10.12上运行,在chrome中得到类似的结果。

1 个答案:

答案 0 :(得分:3)

V8开发人员在这里。简短的答案是.push()是超级优化的,而写入.length的操作相当慢(部分原因是JavaScript规范说的必须这样做,部分原因是我们尚未对其进行优化一样多-但是即使我们这样做,对于某些元素,它的速度也不会像.push()那样。

实际上,您会注意到写入.length以缩短数组与多次调用.pop()之间存在相似的区别。

我个人认为这是一个不错的状态:您基于.push的代码简洁,直观且可读。基于.length的替代方案似乎试图以使代码更丑陋和更复杂为代价榨取一些额外的性能-这样做没有有用吗? ?编写您要编写的代码,让引擎为快速而烦恼! :-)

  

使用.length = ...与.push(...)时,V8是否执行不同类型的数组大小调整?

写入.length和调用.push(...)是非常不同的操作(后者非常简单,前者必须执行一系列检查),是的,在V8的幕后工作必定有所不同。调整大小本身(如果/一旦发生)是相同的。

  

这与V8处理稀疏数组有关吗?

不在您的示例中。通常,写入.length必须检查数组是否应该转换为稀疏模式,但是检查本身非常快。