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
。同样,按该顺序添加 x
和 y
属性在不必创建新的隐藏 class.
方面也将是有效的
但是这个方案有两个潜在的问题。第一个涉及以下代码段发生的情况:
Point p3 = new Point();
p3.y = 3141592653589;
p3.x = 2718281828459;
在我看来,这将创建两个 new 隐藏的 classes,一个 y
偏移量为零,另一个 y
] 在偏移量零和 x
在偏移量一。
这似乎有点 space-低效,因为在这两种情况下最终的 classes 都有一个 x
和一个 y
所以应该能够共享 class C2
,尽管要求您需要在对象本身中交换 x
和 y
。
使过渡映射更受控制会产生什么影响,例如确保属性按字母顺序存储?这样,无论您先添加 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
我一直在研究 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
。同样,按该顺序添加 x
和 y
属性在不必创建新的隐藏 class.
但是这个方案有两个潜在的问题。第一个涉及以下代码段发生的情况:
Point p3 = new Point();
p3.y = 3141592653589;
p3.x = 2718281828459;
在我看来,这将创建两个 new 隐藏的 classes,一个 y
偏移量为零,另一个 y
] 在偏移量零和 x
在偏移量一。
这似乎有点 space-低效,因为在这两种情况下最终的 classes 都有一个 x
和一个 y
所以应该能够共享 class C2
,尽管要求您需要在对象本身中交换 x
和 y
。
使过渡映射更受控制会产生什么影响,例如确保属性按字母顺序存储?这样,无论您先添加 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