当我查看v8 design elements of fast property access时,它在本主题的最后一段中提到了这一点:
使用隐藏类有两个好处:属性访问 不需要字典查找,它们使V8能够使用经典 基于类的优化,内联缓存。
这对我来说有点模糊。任何人都可以详细说明为什么隐藏类不需要字典查找并使v8能够使用经典的基于类的优化,内联缓存?
请尽可能详细说明。
答案 0 :(得分:3)
我认为隐藏类的概念在您引用的文章中得到了很好的解释。每当将新属性添加到对象时,都会创建一个新的隐藏类。该对象保留对此隐藏类的引用。此外, hidden-class 还会保留对先前创建的 hidden-class 的引用。例如:
function Point(x, y) {
this.x = x;
this.y = y;
}
var point = new Point(10, 10);
当new Point()
被调用时,会创建一个新的 hidden-class ,point
会保留对该 hidden-class 的引用。最初的 hidden-class 是空的,这意味着它不包含任何属性。然后,当调用this.x
时,会创建一个新的隐藏类。该隐藏类保留对前一个隐藏类的引用,并指向更新其对新隐藏类的引用。 <{1}}执行时会再次发生同样的情况。
在JavaScript中可以通过dinamically添加和删除对象的属性,解决属性访问的方法是使用 map 。另一方面, hidden-classes 一个接一个地线性存储属性,就像C中的 struct 一样。感谢 hidden-classes ,访问属性与访问数组元素一样快。
现在让我们来看看内联缓存的含义。内联缓存是一种古老的优化技术,用于动态语言,如Smalltak 80或Self,并由JavaScript推广。在运行时访问属性时,有必要确定调用对象的类型,以便确切地知道要调用的实现代码。这称为动态调度或后期绑定,是访问属性时或在总结两个操作数时在JavaScript中发生的事情(它们可能是整数, double 等)。请考虑以下代码:
this.y = y
使用内联缓存时,编译var x = 10;
var y = 10;
var total = x + y;
时,不要在对通用添加子例程的过程调用中编译。相反,该代码被编译为存根(内联缓存)。 内联缓存中生成的代码会查看所接收参数的类型,并生成专门用于这些类型的代码。稍后,如果调用另一个添加,则执行内联缓存,假设类型与以前相同。可能会发生类型不同的情况,因此内联缓存的第一件事就是检查参数的类型。如果类型不同导致缓存未命中,则会为这些特定类型生成新代码。如果内联缓存可以处理许多不同类型,称为多态内联缓存(一个包含多个类型的条目)。
内联缓存保存了在函数调用中推断参数类型的计算,如果调用在循环中发生,它们的好处就更大。
内联缓存在访问对象属性时在JavaScript中发生,在我们看到 hidden-classes 如何帮助我们快速访问属性之前。
有关 hidden-classes 和 inline-caches 的更多信息,我推荐以下文章(特别是前两篇,来自V8黑客Vyacheslav Egorov):