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

uks*_*ksz 21 javascript polymorphism monomorphism

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

(..)原因是它必须以动态方式编写,因此无论模型结构如何,它都可以检查每个组件.虚拟机不喜欢这种动态代码,因为它们无法对其进行优化.它被认为是多态的,因为对象的形状并不总是相同的.Angular在运行时为每个组件创建变化检测器类,这些组件是单态的,因为它们确切地知道组件模型的形状.虚拟机可以完美地优化此代码,从而使执行速度非常快.好处是我们不必过多关心它,因为Angular自动完成它.(..)

资源

现在,我试图找到monomoprhic vs polymorphic的例子,但在任何地方都找不到它.任何人都可以解释差异,为什么它更快?

Kle*_*vič 20

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

其原因在此v8特定指南文档中有详细记录,其中解释了整数与一般数字优化.说你有:

function add(a, b) { return a + b; }
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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


Fra*_*yce 5

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

多态性:是可以用机器/运行时/解释器的同一类型表示许多(多)不同对象的想法。例如,在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)
Run Code Online (Sandbox Code Playgroud)

您提供的链接中引号的要点是,AngularJS提供了开箱即用的代码,该代码使所有javascript函数参数都是“单态的”,因为每次传入它们的对象碰巧都具有相同的结构。