V8 引擎如何处理添加属性的排序?

How does the V8 engine handle sequencing of added properties?

我一直在研究 Google 的 V8 Javascript 引擎的性能改进,因为我想将它纳入我自己的项目之一。

我目前的兴趣是隐藏的 classes。基本思想是 V8 在将属性添加到对象时创建隐藏的 classes,以便有效地查找 属性,避免字典搜索。

例如,当您创建一个名为 p 的新 Point 对象时,它会创建一个隐藏的 class C0,它是一个 class 没有属性并将 class 附加到对象:

语句p.x = 0将修改对象添加属性,然后创建newclassC1指定x 属性 可以在对象内的特定偏移处找到。

所以在那之后到达p.x是一个相对有效的操作。

最后,执行 p.y = 0 将执行类似的操作,最终结果为:

现在这实际上相当漂亮,因为如果您创建另一个 Point 对象 p2,则不需要创建新的隐藏 class,它只会得到 "assigned"至 C0。同样,按该顺序添加 xy 属性在不必创建新的隐藏 class.

方面也将是有效的

但是这个方案有两个潜在的问题。第一个涉及以下代码段发生的情况:

Point p3 = new Point();
p3.y = 3141592653589;
p3.x = 2718281828459;

在我看来,这将创建两个 new 隐藏的 classes,一个 y 偏移量为零,另一个 y ] 在偏移量零和 x 在偏移量一。

这似乎有点 space-低效,因为在这两种情况下最终的 classes 都有一个 x 和一个 y 所以应该能够共享 class C2,尽管要求您需要在对象本身中交换 xy

使过渡映射更受控制会产生什么影响,例如确保属性按字母顺序存储?这样,无论您先添加 x,然后添加 y,还是添加 y,然后添加 x,您仍然会得到相同的最终结果 class.

这意味着在添加 属性 时需要做一些额外的工作,但可以大大减少隐藏 classes 的数量。

或者,额外的工作本身是否会消耗过多的性能?


第二个潜在问题是,由于此方案的全部目的是避免对对象进行字典查找,因此如何移动:

For x, see offset 0
For y, see offset 1

进入class帮助?

在我看来,您仍然需要在隐藏的 class 中查找 属性 名称以获得对象内的偏移量。

或者我是否遗漏了一些东西并且不需要 class 的字典搜索?

优化后,无需查字典(大部分访问)。

在 属性 访问时间,v8 已(希望)将 属性 访问转换为内联缓存存根。这意味着在访问 x 时,它作为 x 的标识已被包含在偏移值中。所以它不再是真正的x,甚至x at offset 0,它只是offset 0。即使对象表示称为 "map,",它实际上只是一个偏移量 table。这就是 属性 访问速度快的原因。

当需要添加新的 属性 时,问题不是 "where's an object with all the properties the current object has plus the new property?" 而是 "for this object, what do I need to do to add this one property?" 在此基础上选择过渡。在优化代码中,不考虑现有属性的名称。删除多余的隐藏 类 需要一些相对繁重的操作才能找到匹配的地图。

您假设反转 属性 加法会创建两个新的隐藏 类 是正确的。您可以通过 运行 以下 d8 --allow-natives-syntax:

获得一些见解
function Point() {}

var p = new Point();

var px = new Point();
px.x = 0x10101;

var py = new Point();
py.y = 0x20202;

var pxy = new Point();
pxy.x = 0x30303;
pxy.y = 0x40404;

var pyx = new Point();
pyx.y = 0x50505;
pyx.x = 0x60606;

var newp = new Point();
checkmaps();
newp.x = 0x70707;
checkmaps();
newp.y = 0x80808;
checkmaps();

function checkmaps() {
  var sameas = [];
  if (%HaveSameMap(newp, p))
    sameas.push("p");
  if (%HaveSameMap(newp, px))
    sameas.push("px");
  if (%HaveSameMap(newp, py))
    sameas.push("py");
  if (%HaveSameMap(newp, pxy))
    sameas.push("pxy");
  if (%HaveSameMap(newp, pyx))
    sameas.push("pyx");
  print(sameas);
}

输出为:

p
px
pxy