26

我一直在阅读一些关于变化检测的文章,他们都说单态函数比多态函数快得多。例如,这是一个引用:

(..) 这样做的原因是,它必须以动态方式编写,因此它可以检查每个组件,无论其模型结构是什么样的。VM 不喜欢这种动态代码,因为它们无法优化它。它被认为是多态的,因为对象的形状并不总是相同的。Angular 在运行时为每个组件创建变化检测器类,它们是单态的,因为它们确切地知道组件模型的形状是什么。VM 可以完美地优化此代码,使其执行速度非常快。好消息是我们不必太在意这些,因为 Angular 会自动完成。(..)

资源

现在,我试图找到单态与多态的例子,但在任何地方都找不到。任何人都可以解释其中的区别,为什么它更快?

4

2 回答 2

24

答案在于虚拟机可以对“热函数”进行启发式检测,即执行数百甚至数千次的代码。如果一个函数的执行计数超过了预定的限制,VMs 优化器可能会拾取那段代码并尝试根据传递给函数的参数编译一个优化版本。在这种情况下,它假定您的函数将始终使用相同类型的参数(不一定是相同的对象)调用。

其原因在这个v8 特定指南文档中有详细记录,其中解释了整数与一般数字优化。假设你有:

function add(a, b) { return a + b; }

...并且您总是使用整数调用此函数,可以通过编译一个在 CPU 上进行整数求和的函数来优化此方法,这很快。如果在优化后你给它一个非整数值,那么 VM 会取消优化函数并回退到未优化的版本,因为它不能对非整数执行整数求和,并且函数会返回错误的结果。

在您指定重载单态方法的语言中,您可以通过简单地编译具有不同参数签名的同一方法名称的多个版本,然后自行优化来解决此问题。这意味着您调用不同的优化方法,因为使用不同类型的参数需要您使用不同的重载方法,因此您使用哪种方法没有问题。

您可能认为您可以在 VM 中保留多个优化函数的副本并检查类型以确定要使用的优化编译函数。从理论上讲,如果方法调用之前的类型检查是免费的或非常便宜的话,那将是可行的。在实践中,通常情况并非如此,您可能希望根据实际代码平衡事物以确定最佳权衡阈值。

这是一个更通用的解释,特别是 v8 的优化编译器(来自 Google I/O 2012):

https://youtu.be/UJPdhx5zTaw?t=26m26s

简而言之:一遍又一遍地使用相同类型调用的函数在 JIT 编译器中进行了优化,因此速度更快。

于 2016-05-05T19:48:04.637 回答
6

据我所知,单态是一个非常不常见的术语。我个人从未听说过它用于编码。不过,要弄清楚什么是单态,我认为我们可以通过查看多态是什么来推断它的含义。

多态性:是指许多(多)不同的对象可以用相同的类型表示给机器/运行时/解释器。例如,在 C# 中,您可以拥有任意数量的类来实现ICloneable,并且它们中的任何一个都可以在通用链表的复制构造函数中使用(例如)。 如果您有兴趣,这里以完整的未经测试的课程为例

好的,那么单态是什么意思?

对我来说,单态意味着对象的解释器处理它所期望的 EXACT 类型,并且不可能继承或修改预期的类型。在这种情况下,使用鸭子类型的 javascript 语言,VM 说“这个 javascript 对象具有这些确切的属性,这些属性属于这些确切的类型并且命名与它们完全一样”。在 C# 中,如果我们想要单态,那么泛型类型限制将是不可能的,因为T必须始终是相同的类型。

这个链接提供了一个很好的指南,说明为什么这对性能很重要。对我来说,这可以总结如下。

Javascript 引擎希望避免对属性进行表查找,而是进行对象指针偏移。使用单态性,给定代码行中对象的对象偏移量将始终相同,并且 VM 很容易弄清楚如何使用指针添加而不是表查找来执行查找。

实际上,引擎尝试处理传递给同一函数的少量不同对象,但如果对象在同一行代码中始终看起来相同,则 VM 将是最快的。

为清楚起见的示例

以下示例是有效的 javascript,但ofunction的参数f1不是单态的,因为 VM 需要处理传入的两个不同形状的对象。

function f1(o) {
  console.log(o.prop1)
  console.log(o.prop2)
}

// ...

o1 = { prop1: 'prop1', prop2: 'prop2' }
o2 = { prop1: 'prop1', prop2: 'prop2', prop3: 'prop3' }

f1(o1)
f1(o2)

您提供的链接中引用的要点是,开箱即用的 AngularJS 提供的代码使其所有 javascript 函数参数都是“单态的”,因为传递给它们的对象在每次被调用时碰巧具有相同的结构.

于 2016-05-05T20:04:41.057 回答