数组调整大小的性能,设置长度属性与重复推送 [英] Array resize performance, setting length property vs. repeated pushing

查看:91
本文介绍了数组调整大小的性能,设置长度属性与重复推送的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我对以下代码进行了基准测试,试图找出哪些代码更具性能:

So I was benchmarking the following code, trying to figure out which would be more performant:

'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');

结果让我感到有些惊讶:

The result surprised me a little:

addSetToArrayA: 728.773ms
addSetToArrayB: 3296.437ms

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

So I did another benchmark:

'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');

结果与我之前的经历一致:

The results were in line with what I experienced previously:

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()慢,而对于较大的数组它更快(正如预期的那样)。

Eg. for relatively small arrays re-sizing via .length is slower than repeatedly using .push(), while for larger arrays it's faster (as expected).

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

Is V8 performing different types of array resizing when using .length = ... vs. .push(...)? Is this related to how V8 handles sparse arrays?

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

Running on Node@10.12 with similar results in chrome.

推荐答案

V8开发人员在这里。简短的回答是 .push()是超级优化的,而写入 .length 是一个相当慢的操作(部分是因为JavaScript规范必须要做的,部分是因为我们没有对它进行过多优化 - 但即使我们这样做了,它也不会像那样快速.push() 代表一些元素。)

V8 developer here. The short answer is that .push() is super optimized, whereas writing to .length is a fairly slow operation (partially because of what the JavaScript spec says it must do, and partially because we haven't optimized it quite as much -- but even if we did, it wouldn't become as fast as .push() for a few elements).

事实上,你会注意到写入 .length <之间的类似差异/ code>缩短一个数组并多次调用 .pop()

In fact, you'll notice a similar difference between writing to .length to shorten an array and calling .pop() a couple of times.

个人订单我认为这不是一个糟糕的状态:基于 .push 的代码简洁,直观且易读。基于 .length 的替代方案看起来像是试图以使代码变得更加丑陋和更复杂的方式来挤出一些额外的性能 - 这不是很好吗有帮助吗?写下你想写的代码,让引擎担心快速编写代码! : - )

Personally I think this is not a bad state to be in: your .push based code is concise, intuitive, and readable. The .length based alternative looks like an attempt to squeeze out a bit of extra performance at the cost of making the code uglier and more complicated -- isn't it nice that that does not help? Write the code you want to write, let the engine worry about making it fast! :-)


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

Is V8 performing different types of array resizing when using .length = ... vs. .push(...)?

写入 .length 并致电。 push(...)是非常不同的操作(后者非常简单,前者必须执行一系列检查),所以是的,V8在引擎盖下做的事情必然是不同的。调整大小本身,如果/一旦发生,是相同的。

Writing to .length and calling .push(...) are very different operations (the latter is pretty straightforward, the former must perform a bunch of checks), so yeah, what V8 does under the hood is necessarily different. The resizing itself, if/once it happens, is the same.


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

Is this related to how V8 handles sparse arrays?

不在你的例子中。一般来说,写入 .length 必须检查数组是否应该转换为稀疏模式,但检查本身的速度非常快。

Not in your example. In general, writing to .length must check whether the array should transition to sparse mode, but that check itself is pretty fast.

这篇关于数组调整大小的性能,设置长度属性与重复推送的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆