2012-04-12 12 views
10

Also programmiere ich eine 2d Javascript Physik Simulation. Die Leistung ist gut, aber ich mache Optimierungen, um es besser zu machen. Also, weil das Programm mit viel physikalischer Geometrie arbeitet, mache ich mehrere Pythagoras-Theorem-Berechnungen im Programm. Insgesamt etwa fünf Berechnungen; zusammen laufen sie etwa eine Million Mal pro Sekunde. Also dachte ich, es würde die Leistung steigern, wenn ich diesen einfachen Satzsatz des Pythagoras in eine neue Funktion übertrage und ihn nenne; Schließlich muss der Browser weniger kompilieren. Also, ich lief den Code in Firefox und bekam .... eine 4000000% Erhöhung in der Ausführungszeit dieser Berechnung.JS: Wie lange dauert es, eine Funktion aufzurufen?

Wie? Es ist der gleiche Code: Math.sqrt (x * x + y * y), also wie verlangsamt es das Hinzufügen als Funktion? Ich nehme an, der Grund ist, dass eine Funktion nur Zeit benötigt, um aufgerufen zu werden, ohne den Code auszuführen, und dass das Hinzufügen von einer Million dieser Verzögerungen pro Sekunde es verlangsamt?

Das scheint mir ziemlich alarmierend. Gilt dies auch für vordefinierte js-Funktionen? Es scheint unwahrscheinlich, und wenn ja, wie vermeiden sie es?

Der verwendete Code wie folgt zu gehen:

function x() 
{ 
    dx=nx-mx; 
    dy=ny-my; 
    d=Math.sqrt(dx*dx+dy*dy); 
    doStuff(... 
} 

Was habe ich versucht, war dies:

function x() 
{ 
    dx=nx-mx; 
    dy=ny-my; 
    d=hypo(dx,dy); 
    doStuff(... 
} 
function hypo(x,y) 
{ 
    return Math.sqrt(x*x+y*y); 
} 

Dank!

+3

Ist Ihre Funktion außerhalb des Bereichs definiert, der millionenmal pro Sekunde ausgeführt wird? – alex

+0

Und es ist nicht wahr, dass der Browser "weniger Kompilierung zu tun" hat, weil Sie es in eine Funktion einfügen ... es sollte in etwa gleich sein, besonders, da Kompilierung eine Startup-Sache ist. Aber @alex hat wahrscheinlich den Grund für Ihre 400% ige Verlangsamung :) – Ryan

+0

@alex Ja, es ist im Hauptfenster definiert. – mindoftea

Antwort

7

Funktionsaufrufe sind vernachlässigbar oder sogar optimieren in vorkompilierten Sprachen, die JS noch nie war. Darüber hinaus hängt viel vom Browser ab.

Sie sind der Tod aller Leistung in interpretierten Sprachen, die JS in erster Linie bis vor kurzem war. Die meisten modernen Browser haben JIT (Just In Time) -Compiler, was ein großes Upgrade von den JS-Interpretern der Vergangenheit ist, aber ich glaube, dass Funktionsaufrufe zu einem anderen Bereich noch einige Kosten verursachen, da das Aufrufobjekt von JS bestimmen muss, was tatsächlich aufgerufen wird verschiedene Scope-Ketten auf- und abmarschieren.

Also als eine allgemeine Regel: Wenn Sie über IE8 und niedrigere und ältere Versionen von Chrome und Firefox kümmern vermeiden Sie Funktionsaufrufe Zeitraum. Vor allem innerhalb von Schleifen. Für die JIT-Browser würde ich erwarten, dass eine Funktion, die in der anderen Funktion definiert ist, allgemein von Vorteil wäre (aber ich würde immer noch testen, da dies eine brandneue Technologie für IE9 ist und relativ neu für alle anderen).

Eine andere Sache, vor der man sich hüten sollte. Wenn eine Funktion besonders komplex ist, können JITs nichts tun, um sie zu optimieren.

https://groups.google.com/forum/#!msg/closure-compiler-discuss/4B4IcUJ4SUA/OqYWpSklTE4J

Aber das Wichtigste ist, zu verstehen, dass, wenn etwas gesperrt ist und nur in einem Kontext, wie eine Funktion innerhalb einer Funktion aufgerufen, soll es einfach sein für eine JIT zu optimieren. Außerhalb einer Funktion definiert, muss festgelegt werden, welche Definition dieser Funktion genau aufgerufen wird. Es könnte in einer äußeren Funktion sein. Es könnte global sein. Es könnte eine Eigenschaft des Prototyps des Konstruktors des Fensterobjekts sein, usw. In einer Sprache, in der Funktionen erste Klasse sind, dh ihre Referenzen als Argumente weitergegeben werden können, genauso wie Sie Daten weitergeben, können Sie diesen Schritt nicht wirklich vermeiden außerhalb Ihres aktuellen Kontextes.

Versuchen Sie also, Hypo in X zu definieren, um zu sehen, was passiert.

Noch ein paar allgemeine Tipps aus dem interpretierten Alter, die noch in JITs wertvoll sein könnte: ‚‘

  • Die Operator wie in someObject.property, ist ein Prozess Caching wert. Es kostet Overhead, da es bei jeder Verwendung einen zugehörigen Aufrufobjekt-Lookup-Prozess gibt. Ich stelle mir vor, Chrome würde die Ergebnisse dieses Prozesses nicht beibehalten, da Änderungen an übergeordneten Objekten oder Prototypen das, was es tatsächlich referenziert, außerhalb eines bestimmten Kontextes verändern könnten. Wenn in Ihrem Beispiel x von einer Schleife verwendet wird (wahrscheinlich in Ordnung oder sogar hilfreich, wenn x in der gleichen Funktion wie die Schleife in JITs definiert ist - Mord in einem Interpreter), würde ich versuchen, Math.sqrt einer Variablen zuzuordnen, bevor Sie sie verwenden in Hypo. Zu viele Verweise auf Dinge außerhalb des Kontextes Ihrer aktuellen Funktion könnten dazu führen, dass einige JITs entscheiden, dass es sich nicht lohnt, zu optimieren, aber das ist reine Spekulation meinerseits.

  • Das folgende ist wahrscheinlich der schnellste Weg, um Schleife ein Array:

//assume a giant array called someArray 
var i = someArray.length; //note the property lookup process being cached here 
//'someArray.reverse()' if original order isimportant 
while(i--){ 
    //now do stuff with someArray[i]; 
} 

Hinweis: Codeblock hier nicht aus irgendeinem Grund zu arbeiten.

Dies zu tun kann hilfreich sein, da es im Grunde den Inc/Dekrement-Schritt und den logischen Vergleich in nur das Dekrement verwandelt, wodurch die Notwendigkeit eines Links/Rechts-Vergleichsoperators vollständig entfällt. Beachten Sie, dass in JS der Dekrementoperator auf der rechten Seite bedeutet, dass ich zur Bewertung weitergeleitet und dann dekrementiert werde, bevor er innerhalb des Blocks verwendet wird. while(0) wird zu false ausgewertet.

+0

Danke; eine sehr schöne Erklärung! Die Definition der Hypofunktion innerhalb der größeren Funktion benötigt zu viel Zeit und ich würde sie in anderen Funktionen benötigen, so dass es sich für mich nicht lohnt. Aus Neugierde habe ich jedoch versucht, es im Inneren zu definieren. Das hat 97% der Leistungsprobleme gelöst! Es ist immer noch aufgeblasen, aber viel besser. Wissen Sie, wie vordefinierte Funktionen dies vermeiden? Danke noch einmal! – mindoftea

+0

Kern-JS-Objekte und -Methoden arbeiten tatsächlich auf einer viel niedrigeren Ebene. Wenn Sie beispielsweise versuchen, Math.sqrt zu alarmieren (keine Parens), werden Sie wahrscheinlich eine Funktion mit [NATIVE CODE] oder etwas auf der Innenseite sehen. Das ist eigentlich ein Anruf, der normalerweise aus der vorkompilierten Laufzeitumgebung des Browsers stammt. Es kann immer noch hilfreich sein, diese Lookups zu cachen, weil Sie diese Verweise auf die nativ definierten Methoden durch JS ersetzen können (nicht sehr schlau, aber Sie können das tun). Was bedeutet, JITs müssen immer noch den Lookup-Prozess berücksichtigen. –

+0

Auch, wenn Sie noch alles eingerichtet haben, versuchen Sie Hypo definiert inline mit 'var sqrt = Math.sqrt' direkt davor und rufen Sie dann sqrt statt innerhalb Hypo. Ich bin neugierig. –

1

Zu meiner Überraschung Caching der Lookup wie von Erik vorgeschlagen nicht viel tun Leistung in meinem Browser zu verbessern (Chrom, Linux) aber scheint verletzt Leistung statt: http://jsperf.com/inline-metric-distance

var optimizedDistance = (function() { 
    var sqrt = Math.sqrt; 
    return function (x, y) { return sqrt(x * x + y * y); } 
})(); 

ist langsamer als

var unoptimizedDistance = function(x, y) { 
    return Math.sqrt(x * x + y * y); 
} 

Selbst ein Alias ​​Aufruf langsamer

var _sqrt = Math.sqrt; // _sqrt is slower than Math.sqrt! 

Aber das ist wiederum keine exakte Wissenschaft und reale Messungen können noch variieren.

Nichtsdestotrotz würde ich mit Math.sqrt gehen.

+0

Interessant. Ich werde in einer Sekunde meine eigenen Tests machen. – mindoftea

+0

In Ordnung. Ich habe den gesamten Code mit und ohne Math-Caching getestet. Ich habe sie als Globals definiert, damit meine verschiedenen Funktionen auf sie zugreifen und sie dann ausführen können. Es scheint, dass das Caching in einigen Fällen etwas langsamer war. – mindoftea

Verwandte Themen