为什么JavaScript中的单态和多态问题?

时间:2016-05-05 19:20:08

标签: javascript polymorphism monomorphism

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

  

(..)原因是,它必须以动态方式编写,所以   它可以检查每个组件,无论其模型结构如何   喜欢。虚拟机不喜欢这种动态代码,因为它们不能   优化它。它被认为是多态的物体形状   并不总是一样的。 Angular创建了变化检测器类   每个组件的运行时都是单态的,因为他们知道   究竟是组件模型的形状。虚拟机可以完美   优化此代码,使其执行速度非常快。好的   事情是我们不必过多关心那个,因为   Angular自动完成。(..)

Source

现在,我试图找到单一性与多态性的例子,但无法在任何地方找到它。任何人都可以解释差异,为什么它更快?

2 个答案:

答案 0 :(得分:17)

答案在于VM可以对“热门功能”进行启发式检测,这意味着代码可以执行数百甚至数千次。如果函数的执行计数超过预定限制,则VM优化器可能会获取该位代码并尝试根据传递给函数的参数编译优化版本。在这种情况下,它假设您的函数将始终使用相同的类型参数调用(不一定是相同的对象)。

这个原因在v8-specific guideline document中有详细记载,其中解释了整数与一般数字优化。说你有:

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

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

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

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

这是一个更普遍的解释v8的优化编译器(来自Google I / O 2012):

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

简而言之:在JIT编译器中优化使用相同类型一次又一次调用的函数,因此更快。

答案 1 :(得分:4)

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

多态性:许多(多个)不同对象可以用相同的类型表示给机器/运行时/解释器。例如,在C#中,您可以拥有所需的实现ICloneable的类,并且任何类都可以在通用链表的复制构造函数中使用(例如)。 Full un-tested class here as an example if you're interested

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

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

This link提供了一个很好的指导,说明为什么这对绩效至关重要。对我来说,这可以总结如下。

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

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

清晰度示例

以下示例是有效的javascript,但函数o的参数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函数参数都是"单态"因为传递给它们的对象在每次调用时碰巧都具有相同的结构。