JavaScript中的对象/数组的性能如何?(专门针对Google V8)

BMi*_*ner 104 javascript arrays performance v8 object

与JavaScript中的数组和对象(尤其是Google V8)相关的性能对于文档来说非常有趣.我在互联网上找不到关于这个主题的综合文章.

我知道一些对象使用类作为它的底层数据结构.如果有很多属性,它有时被视为哈希表?

我也理解,数组有时被视为C++数组(即快速随机索引,慢速删除和调整大小).而且,其他时候,它们更像对象(快速索引,快速插入/删除,更多内存).并且,有时它们可​​能存储为链接列表(即慢速随机索引,在开头/结尾快速删除/插入)

JavaScript中的数组/对象检索和操作的精确性能是什么?(专门针对Google V8)

更具体地说,它对性能的影响如下:

  • 向Object添加属性
  • 从Object中删除属性
  • 索引对象中的属性
  • 将项添加到数组
  • 从数组中删除项目
  • 索引数组中的项目
  • 调用Array.pop()
  • 调用Array.push()
  • 调用Array.shift()
  • 调用Array.unshift()
  • 调用Array.slice()

任何文章或链接的更多细节也将不胜感激.:)

编辑:我真的很想知道JavaScript数组和对象是如何工作的.此外,在什么情况下 V8引擎"知道""转换"到另一个数据结构?

例如,假设我用...创建一个数组

var arr = [];
arr[10000000] = 20;
arr.push(21);
Run Code Online (Sandbox Code Playgroud)

这里到底发生了什么?

或者......这个怎么样...... ???

var arr = [];
//Add lots of items
for(var i = 0; i < 1000000; i++)
    arr[i] = Math.random();
//Now I use it like a queue...
for(var i = 0; i < arr.length; i++)
{
    var item = arr[i].shift();
    //Do something with item...
}
Run Code Online (Sandbox Code Playgroud)

对于传统阵列,性能会很糟糕; 然而,如果使用了LinkedList ......那就不那么糟了.

Pic*_*tor 275

更新:请注意,JSPref当前已关闭

(我已经保存了测试用例的副本,并且一旦JSPref被修复/找到了后继者,它将更新答案)


嗯...对于答案来说可能有点过分......但我创建了一个测试套件,正是为了探索这些问题(以及更多)(存档副本).

从这个意义上讲,您可以在这个50多个测试用例测试器中看到性能问题(这需要很长时间).

另外正如其名称所示,它探讨了使用DOM结构的本机链表性质的用法.

(目前已关闭,正在重建)我的博客上有关于此的更多详细信息.

摘要如下

  • V8阵列速度快,非常快
  • 数组推/弹/移位比任何对象等约快20倍+.
  • 令人惊讶的Array.shift()是快速〜比阵列弹出快约6倍,但比对象属性删除快约100倍.
  • 有趣的Array.push( data );是,比Array[nextIndex] = data几乎20(动态阵列)到10(固定阵列)时间快.
  • Array.unshift(data) 比预期慢,并且比新属性添加慢约5倍.
  • 取消值array[index] = nulldelete array[index]在数组中删除它(未定义)快〜约4x ++更快.
  • 令人惊讶的是,在对象中取消一个值obj[attr] = null比仅删除属性大约慢2倍delete obj[attr]
  • 不出所料,mid array Array.splice(index,0,data)很慢,非常慢.
  • 令人惊讶的是,它Array.splice(index,1,data)已经过优化(无长度变化),比拼接速度快100倍Array.splice(index,0,data)
  • 不出所料,divLinkedList不如所有扇区上的数组,除了dll.splice(index,1)删除(它打破了测试系统).
  • 最大的惊喜 [正如jjrv所指出的],V8阵列写入比V8读取稍微快= O

注意:这些指标仅适用于v8不"完全优化"的大型数组/对象.对于阵列/对象大小小于任意大小(24?),可能存在非常孤立的优化性能情况.可以在多个Google IO视频中广泛查看更多详细信息.

注意2:这些精彩的性能结果不会在浏览器之间共享,尤其是 *cough*IE.此外测试是巨大的,因此我还没有完全分析和评估结果:请在=)中编辑它

更新的注释(2012年12月): Google代表在youtubes上有视频,描述了Chrome本身的内部工作原理(比如从链接列表数组切换到固定数组等),以及如何优化它们.有关更多信息,请参阅GDC 2012:从控制台到Chrome.

更新的注释(2013年2月): Thx @badunk,用于在确切的位置提供视频链接

更新的注释(2016年6月): Thx @Benedikt,关于固定/动态阵列中的阵列推送性能差异.

  • 其中一些结果看起来很奇怪.例如,在Chrome中,数组写入速度比读取快10倍,而在Firefox中则相反.在某些情况下,您确定浏览器JIT没有优化整个测试吗? (2认同)
  • 只是想在数组视频讨论中添加确切的观点:https://www.youtube.com/watch?feature = player_detailpage&v = XAqIpGU8ZZk#t = 994s (2认同)
  • JsPerf 站点不再存在:( (2认同)

小智 5

在保持在JavaScript领域内的基本级别上,对象的属性是更复杂的实体.您可以使用setter/getter创建属性,具有不同的可枚举性,可写性和可配置性.无法以这种方式自定义数组中的项:它是存在还是不存在.在底层引擎级别,这允许在组织表示结构的存储器方面进行更多优化.

在从对象(字典)中识别数组方面,JS引擎总是在两者之间做出明确的界限.这就是为什么有很多关于尝试制作半假的类似于对象的方法的文章,这些对象的行为类似于一个但允许其他功能.这种分离甚至存在的原因是因为JS引擎本身以不同的方式存储这两者.

属性可以存储在数组对象上,但这只是演示了JavaScript如何坚持将所有内容都设为对象.数组中的索引值的存储方式与您决定在表示基础数组数据的数组对象上设置的任何属性的存储方式不同.

每当你使用一个合法的数组对象并使用一种操作该数组的标准方法时,你就会遇到底层的数组数据.特别是在V8中,这些与C++数组基本相同,因此这些规则将适用.如果由于某种原因你正在使用一个引擎无法自信地确定阵列的阵列,那么你就会更加不稳定.随着最新版本的V8,有更多的工作空间.例如,可以创建一个以Array.prototype作为其原型的类,并且仍然可以有效地访问各种本机数组操作方法.但这是最近的变化.

最近更改数组操作的特定链接可能会派上用场:

作为额外的一点,这里是直接来自V8源码的Array Pop和Array Push,两者都是用JS本身实现的:

function ArrayPop() {
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
    throw MakeTypeError("called_on_null_or_undefined",
                        ["Array.prototype.pop"]);
  }

  var n = TO_UINT32(this.length);
  if (n == 0) {
    this.length = n;
    return;
  }
  n--;
  var value = this[n];
  this.length = n;
  delete this[n];
  return value;
}


function ArrayPush() {
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
    throw MakeTypeError("called_on_null_or_undefined",
                        ["Array.prototype.push"]);
  }

  var n = TO_UINT32(this.length);
  var m = %_ArgumentsLength();
  for (var i = 0; i < m; i++) {
    this[i+n] = %_Arguments(i);
  }
  this.length = n + m;
  return this.length;
}
Run Code Online (Sandbox Code Playgroud)