2014-09-13 8 views
5

Beim Lesen von Dokumenten habe ich eine einfache Optimierung gefunden, die Javascript-Leistung erheblich verbessert.Javascript Optimierung mit `neue Funktion()`

Originalcode:

function parseRow(columns, parser) { 
    var row = {}; 
    for (var i = 0; i < columns.length; i++) { 
    row[columns[i].name] = parser.readColumnValue(); 
    } 
} 

optimiertem Code:

var code = 'return {\n'; 
columns.forEach(function(column) { 
    code += '"' + column.name + '":' + 'parser.readColumnValue(),\n'; 
}); 
code += '};\n'; 

var parseRow = new Function('columns', 'parser', code); 

hier gefunden: https://github.com/felixge/faster-than-c
Warum dauert es 20% schneller laufen?
Ich glaube, dass es die for Anweisung entfernt, aber hat die forEach die gleichen Rechenkosten?

+0

Geht es um 20% Gewinn mit der Funktionserstellungslogik selbst? Oder bei jedem Aufruf der Funktion parseRow? Wenn es der letztere Fall ist, würde ich annehmen, dass die Verstärkung aufgrund des Fehlens von for-Schleife in optimiertem Code geschieht. (Im ersten Fall würde die for-Schleife mit jedem Aufruf der parseRow-Funktion ausgeführt werden. Auch mehrere Aufrufe der columns.length-Eigenschaft könnten auf die Langsamkeit des ursprünglichen Codes zurückgeführt werden. Nur meine 2 Cent :)) –

+1

(für V8) http : //www.youtube.com/watch? v = UJPdhx5zTaw –

+1

Wenn die Größe von 'Spalten' groß genug ist, scheint der 'optimierte Code' nicht schneller zu sein. siehe [jsPerf] (http://jsperf.com/javascript-optimization-with-new-function) – rhgb

Antwort

5

Der Unterschied ist, dass Sie nur forEach zu Konstrukt die optimierte Funktion verwenden. Sobald die Funktion erstellt wurde, gibt es keine Schleifen innerhalb: loop is unrolled und Spaltennamen sind hardcoded. Die Methode wird dann in eine Arbeitsfunktion eval ed, die sogar in Maschinencode depending on the engine kompiliert werden könnte. Daraus ergeben sie zwei Leistungsverbesserungen:

  1. Durch die for Schleife Bedingungsprüfung Entfernen (i < columns.length) vollständig, gibt es keine Verzweigung, und
  2. Durch Werte von column[i].name in mehrere Anweisungen zu, entfernen Sie column[i] und Lookups Auswertung zu column.name in jedem Schritt.

So nach new Function(...) mit dem Code als String geben Aufruf, Ihre parseRow Variable erhält den Verweis auf die folgende Funktion:

function parseRow(columns, parser) { 
    return { 
     "columnOne": parser.readColumnValue(), 
     "columnTwo": parser.readColumnValue(), 
     "columnThree": parser.readColumnValue(), 
     ... 
    }; 
} 

Beachten Sie, dass es keine Schleifen, Verzweigungen oder andere Lookups in diesem Code, außer für die multiple parser.readColumnValue() Anrufe.

Warum ist das in JavaScript möglich?

Der Grund, warum dies in JavaScript so effizient funktioniert, ist, weil JavaScript-Quellcode in jeder Webseite von der JS-Engine sowieso interpretiert oder kompiliert werden muss. Sie liefern Ihre Webseite nicht mit kompilierten ausführbaren Dateien oder sogar (noch) vorkompiliertem Bytecode (wie Java oder .NET).Jedes Mal, wenn eine neue .js Datei geladen wird, kompiliert Ihr Browser sie von Grund auf neu, bevor Sie sie ausführen (naja, in modernen Suchmaschinen ist es etwas zwischen dem Interpretieren und Kompilieren, d. H. JITting).

Das bedeutet, dass das Erstellen einer Arbeitsfunktion aus einer Zeichenfolge (d. H. Das Kompilieren des Codes) während der Laufzeit nicht weniger effizient ist als das Lesen von handgeschriebenem Code aus der JS-Datei. Vergleichen Sie das mit einem C/C++ - Programm, das (in allen sinnvollen Fällen) zu Maschinencode kompiliert wurde (d. H. Ausführbare Datei, die der CPU so nahe kommt wie möglich) before it reaches the customer.

Wenn Sie dies in C++ (eine Art von self-modifying code) tun möchten, müssten Sie einen Compiler entlang Ihrer App bündeln, um den Code zu bauen, und die Kosten für den Aufbau dieser Funktion würden die Vorteile übergewichten Du würdest es endlich anfangen. In .NET zum Beispiel ist es auch nicht ungewöhnlich für ein Programm emit methods or even assemblies at run time, das dann JIT kompiliert, um Maschinencode zu kompilieren, der mögliche Leistungsverbesserungen wie die in Ihrer Frage ermöglicht.

2

Die Leistungssteigerungen hängen stark von der JavaScript-Engine sowie von den Daten ab, die verarbeitet werden. Wir kennen die genauen Umstände von "20% schneller" nicht (außer node.js zu verwenden). In manchen Situationen könnte es langsamer sein. (Edit: Sie müssten die Funktion oft genug aufrufen, um die Baukosten aufzuwiegen). Einige mögliche Gründe für die Gewinne:

Der optimierte Code erstellt ein Objekt Literal. Die vorherige Version weist immer noch nicht vorhandenen Eigenschaften Werte zu. Das hat einige Kosten damit verbunden.

row[columns[i].name] hat drei Lookups, während die optimierte Version keine hat, sobald die Funktion konstruiert ist. Und vergiss nicht, dass row[columns[i].name] noch nicht existiert, also ist das Nachschlagen teurer. columns.length ist auch ein Lookup.

Verwandte Themen