2016-05-05 9 views
14

Ich habe einige Artikel über Change Detection gelesen, und alle von ihnen sagen, dass monomorphe Funktionen viel schneller als polymorph sind. Zum Beispiel, hier ist ein Zitat:Warum monomorphe und polymorphe Materie in JavaScript?

(..) Der Grund dafür ist, dass es in einer dynamischen Art und Weise geschrieben werden muss, so es jede Komponente überprüfen kann, egal was seine Modellstruktur sieht mögen. VMs mögen diesen dynamischen Code nicht, weil sie ihn nicht optimieren können. Es wird als polymorph betrachtet, da die Form der Objekte nicht immer gleich ist. Angular erzeugt Änderungsmelder-Klassen bei Laufzeit für jede Komponente, die monomorph sind, weil sie genau wissen, wie die Form des Komponentenmodells ist. VMs können perfekt diesen Code optimieren, wodurch es sehr schnell ausgeführt werden kann. Die gute Sache ist, dass wir nicht viel zu darüber haben zu kümmern, weil Angular es automatisch der Fall ist. (..)

Source

Nun, ich habe versucht, Beispiele für monomoprhic zu finden vs polymorph, aber konnte es nirgendwo finden. Könnte jemand den Unterschied erklären und warum ist er schneller?

+8

Obligatary lesen: http://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html (erste Google-Hit, BTW) – Bergi

Antwort

10

Die Antwort liegt in der Tatsache, dass VMs heuristische Erkennung von "heißen Funktionen" durchführen können, was Code bedeutet, der Hunderte oder sogar Tausende Male ausgeführt wird. Wenn der Ausführungszähler einer Funktion einen vorgegebenen Grenzwert überschreitet, kann der VMs-Optimierer dieses Codebit aufnehmen und versuchen, eine optimierte Version basierend auf den an die Funktion übergebenen Argumenten zu kompilieren. In diesem Fall wird davon ausgegangen, dass Ihre Funktion immer mit demselben Typ von Argumenten aufgerufen wird (nicht notwendigerweise die gleichen Objekte).

Der Grund dafür ist in diesem v8-specific guideline document gut dokumentiert, wo eine ganzzahlige gegen allgemeine Zahl Optimierung erklärt wird. Angenommen, Sie haben:

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

... und Sie rufen diese Funktion immer mit ganzen Zahlen, könnte dieses Verfahren optimiert werden, indem eine Funktion kompilieren, die auf der CPU integer Summe tut, was schnell ist. Wenn Sie nach der Optimierung einen nicht ganzzahligen Wert eingeben, optimiert die VM die Funktion und greift auf die nicht optimierte Version zurück, da sie keine ganzzahlige Summierung für Nicht-Ganzzahlen ausführen kann und die Funktion fehlerhafte Ergebnisse zurückgibt.

In Sprachen, in denen Sie überladene monomorphe Methoden angeben, können Sie dieses Problem umgehen, indem Sie einfach mehrere Versionen desselben Methodennamens mit verschiedenen Argumentsignaturen kompilieren, die dann für sich optimiert werden. Dies bedeutet, dass Sie verschiedene optimierte Methoden aufrufen, da die Verwendung unterschiedlich typisierter Argumente eine andere überladene Methode erfordert. Daher ist es keine Frage, welche Methode Sie verwenden.

Sie könnten denken, dass Sie mehrere Kopien der optimierten Funktionen in der VM behalten könnten, und prüfen Sie, welche optimierten kompilierten Funktionen verwendet werden sollen. In der Theorie würde das funktionieren, wenn die Typüberprüfung vor dem Methodenaufruf frei oder sehr kostengünstig wäre. In der Praxis erweist sich dies normalerweise als nicht zutreffend, und Sie möchten wahrscheinlich die Dinge mit dem realen Code abgleichen, um die beste Kompromissschwelle zu ermitteln.

Hier ist eine allgemeinere Optimierung Compiler Erklärung v8 insbesondere (von Google I/O 2012):

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

Kurz gesagt: Funktionen, die wieder mit den gleichen Typen immer und immer wieder optimiert wird aufgerufen werden im JIT-Compiler, also schneller.

3

Nach meinem Wissen ist Monomorphismus ein sehr ungewöhnlicher Begriff. Ich habe persönlich nie gehört, dass es zum Codieren verwendet wurde. Um herauszufinden, was der Monomorphismus ist, können wir jedoch schlussfolgern, was es bedeutet, wenn wir uns ansehen, was Polymorphismus ist.

Polymorphismus: ist die Idee, dass viele (Poly-) verschiedene Objekte vom gleichen Typ zur Maschine/Laufzeit/Interpreter dargestellt werden können. Zum Beispiel können Sie in C# beliebig viele Klassen implementieren, die ICloneable implementieren, und alle davon könnten in einem Kopierkonstruktor für eine generische verkettete Liste verwendet werden (zum Beispiel). Full un-tested class here as an example if you're interested

Ok also was bedeutet monomorph?

Monomorph zu mir bedeutet, dass der Interpreter des Objekts den EXACT-Typ verarbeitet, den er erwartet, und keine Vererbung oder Änderungen des erwarteten Typs möglich ist. In diesem Kontext, mit der Ente typisierte Javascript-Sprache, sagt die VM "dieses Javascript-Objekt hat diese genaue Eigenschaften, die von diesen genaue Typen sind und genau genannt werden, wie sie sind". Wenn wir in C# monomorph sein wollten, wären generische Typeinschränkungen unmöglich, weil T immer die gleiche Art sein müsste.

This link bietet eine gute Anleitung, warum dies für die Leistung wichtig ist. Zu mir kann das unten zusammengefasst werden.

Javascript-Engines möchten Tabellen-Lookups für Eigenschaften vermeiden und stattdessen Objekt-Zeiger-Offsets ausführen. Beim Monomorphismus sind die Objekt-Offsets für Objekte in einer gegebenen Codezeile immer gleich, und die VM kann leicht herausfinden, wie Lookups mit Pointer-Addition statt mit Tabellen-Lookups durchgeführt werden.

In Wirklichkeit versuchen die Engines eine kleine Anzahl verschiedener Objekte zu bearbeiten, die an dieselbe Funktion übergeben werden, aber die VM wird am schnellsten, wenn das Objekt in derselben Codezeile immer gleich aussieht.

Beispiel zur Klarheit

Das folgende Beispiel gilt JavaScript, aber das Argument of1 funktionieren NICHT monomorphe wird, weil die VM zwei zu handhaben muss, unterschiedlich geformte Gegenstände übergeben werden.

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)

Der Punkt des Angebots von der Verbindung, die Sie zur Verfügung gestellt haben, ist, dass AngularJS out-of-the-box-Code zur Verfügung stellt h macht alle seine JavaScript-Funktionsargumente "monomorph", weil die Objekte, die in sie übergeben werden, jedes Mal die gleiche Struktur haben, wenn sie aufgerufen werden.