Warum verspricht native ES6 langsamer und speicherintensiver als Bluebird?

195

In diesem Benchmark benötigt die Suite bei ES6-Versprechungen 4-mal mehr Zeit als bei Bluebird-Versprechungen und benötigt 3,6-mal mehr Speicher.

Wie kann eine JavaScript-Bibliothek so viel schneller und leichter sein als die native Implementierung von v8 in C? Bluebird-Versprechen haben genau die gleiche API wie native ES6-Versprechen (plus eine Reihe zusätzlicher Dienstprogrammmethoden).

Ist die native Implementierung nur schlecht geschrieben, oder fehlt mir noch ein anderer Aspekt?

Callum
quelle
Beachten Sie, dass moderne JavaScript-Implementierungen stark optimiert sind und möglicherweise sogar nativ mit JIT ausgeführt werden .
1
Laut diesem Benchmark ist BlueBirdJS tatsächlich langsamer als Native Promises. PromiseMeSpeedJS übertrifft tatsächlich beide. Eines der vielen Dinge, die PromiseMeSpeedJS dadurch beweist, ist, dass ein Hauptverantwortlicher für Leistungsversprechen die missbräuchliche Überbeanspruchung des newBetreibers ist, da PromiseMeSpeedJS nicht verwendet new.
Jack Giffin
1
@ JackGiffin Chrome 67: PromiseMeSpeedJS ist 46% langsamer und Bluebird ist 61% langsamer.
FINDarkside

Antworten:

272

Bluebird-Autor hier.

Die Implementierung von V8 verspricht, dass sie in JavaScript und nicht in C geschrieben ist . Jegliches JavaScript (einschließlich des eigenen V8) wird zu nativem Code kompiliert. Zusätzlich wird benutzergeschriebenes JavaScript nach Möglichkeit (und im Wert) optimiert, bevor es zu nativem Code kompiliert wird. Die Implementierung von Versprechungen ist etwas, das nicht viel oder gar nichts davon hat, in C geschrieben zu werden. Tatsächlich würde es nur langsamer, da Sie lediglich JavaScript-Objekte und die Kommunikation manipulieren.

Die V8-Implementierung ist einfach nicht so optimiert wie Bluebird, sondern ordnet Arrays für die Handler von Versprechungen zu . Dies nimmt viel Speicherplatz in Anspruch, wenn jedes Versprechen auch ein paar Arrays zuweisen muss (Der Benchmark erstellt insgesamt 80.000 Versprechen, sodass 160.000 nicht verwendete Arrays zugewiesen werden). In der Realität geben 99,99% der Anwendungsfälle niemals ein Versprechen mehr als einmal ab, sodass die Optimierung für diesen allgemeinen Fall enorme Verbesserungen der Speichernutzung mit sich bringt.

Selbst wenn V8 dieselben Optimierungen wie Bluebird implementieren würde, würde dies durch die Spezifikation behindert. Der Benchmark muss new Promise(ein Anti-Pattern in Bluebird) verwenden, da es in ES6 keine andere Möglichkeit gibt, ein Root-Versprechen zu erstellen. new Promiseist ein extrem langsamer Weg, um ein Versprechen zu erstellen. Erstens weist die Executor-Funktion einen Abschluss zu, zweitens werden zwei separate Abschlüsse als Argumente übergeben. Das sind 3 Verschlüsse pro Versprechen, aber ein Verschluss ist bereits ein teureres Objekt als ein optimiertes Versprechen.

Bluebird kann verwenden, promisifywas viele Optimierungen ermöglicht und eine viel bequemere Möglichkeit darstellt, Callback-APIs zu nutzen, und es ermöglicht die Konvertierung ganzer Module in versprechungsbasierte Module in einer Zeile ( promisifyAll(require('redis'));).

Esailija
quelle
10
"noch durch Spezifikation behindert werden" - nicht sicher, was das bedeutet. Wollen Sie damit sagen, dass ES6 einer Spezifikation folgt, die von Natur aus langsam ist, und wenn dies der Fall ist, bedeutet dies, dass Bluebird nicht der gleichen Spezifikation folgt (und wenn dies der Fall ist, folgt es einer anderen und welcher)? Und gibt es einen Grund, warum ES6 nicht besser new Promisein der Lage sein könnte, ein Root-Promise zu erstellen oder die Instantiierung zu verbessern, um es kostengünstiger zu machen (z. B. keine drei Abschlüsse pro Instanz zu erstellen)?
Anthony
12
Das hört sich überhaupt nicht gut an (für JS). Ich möchte wirklich keine Promise-Bibliothek verwenden, wenn eine interne Implementierung vorhanden ist. Dies ist eine mehr als unglückliche Situation für alle, wenn dies alles wahr ist. Aber ich habe schon Mühe , den Versprechen-Hype zu sehen , wie auch immer, ich habe 100.000 LoC JS apps geschrieben und ich immer noch keine wirkliche Notwendigkeit dafür sehen, es ist eine sehr kleine Verbesserung , wenn überhaupt zu mir , vor allem in der Fehlerbehandlung, nein Verbesserung des Callback-Handlings (ich war noch nie in der "Callback-Hölle" mit meinem Coding-Stil).
Mörre,
19
Können Sie in ES6 kein Promise.resolve()"Root-Versprechen" erstellen?
Zetlen
10
@ MörreNoseshine (Fortsetzung) Jahre später kamen die ES6-Autoren und sagten: "Hey, lassen Sie uns spezifizieren, dass JS-Motoren ein generisches Promises / A + -konformes Utility liefern müssen, damit die Leute immer ein grundlegendes Versprechenstool zur Hand haben ". Dies ist eine nette Annehmlichkeit (keine Bibliothek importieren zu müssen, nur um eine schnelle Promise.resolve()oder was auch immer zu tun ), aber es ist eine sehr einfache Implementierung, und ihre Existenz sollte Sie nicht davon abhalten, ernsthaftere versprechungsbezogene Tools wie Bluebird zu verwenden!
Callum
11
@ MörreNoseshine 100k LOC Javascript-App, die wahrscheinlich nie eine asynchrone Funktionalität hatte. Viel Glück beim Schreiben eines 100k LoC JS-Spiels mit einer mysql / redis-Bibliothek ohne Bluebird.
NiCk Newman