Perché le promesse ES6 native sono più lente e richiedono più memoria rispetto a bluebird?


195

In questo benchmark , la suite impiega 4 volte di più per completare con le promesse ES6 rispetto alle promesse di Bluebird e utilizza 3,6 volte più memoria.

Come può una libreria JavaScript essere molto più veloce e leggera dell'implementazione nativa di v8 scritta in C? Le promesse di Bluebird hanno esattamente la stessa API delle promesse ES6 native (oltre a una serie di metodi di utilità extra).

L'implementazione nativa è stata appena scritta male o c'è qualche altro aspetto in questo che mi manca?


Tieni presente che le moderne implementazioni JavaScript sono fortemente ottimizzate e possono persino essere eseguite in modo nativo utilizzando JIT .

1
Secondo questo benchmark , BlueBirdJS è in realtà più lento delle promesse native. Ma PromiseMeSpeedJS supera in realtà entrambi. Una delle tante cose che PromiseMeSpeedJS dimostra attraverso questo è che un importante colpevole di prestazioni per le promesse è l'abuso abusivo newdell'operatore perché PromiseMeSpeedJS non utilizza new.
Jack Giffin,

1
@JackGiffin Chrome 67: PromiseMeSpeedJS è più lento del 46% e Bluebird è più lento del 61%.
FINDarkside,

Risposte:


272

L'autore di Bluebird qui.

V8 promette che l'implementazione è scritta in JavaScript e non in C. Tutti i JavaScript (incluso quello di V8) sono compilati in codice nativo. Inoltre, se possibile (e ne vale la pena) JavaScript scritto dall'utente viene ottimizzato, prima di essere compilato in codice nativo. L'implementazione delle promesse è qualcosa che non trarrebbe alcun beneficio o affatto dalla scrittura in C, in effetti lo renderebbe solo più lento perché tutto ciò che stai facendo è manipolare gli oggetti JavaScript e la comunicazione.

L'implementazione di V8 non è semplicemente ottimizzata come bluebird, ad esempio alloca array per gestori di promesse . Ciò richiede molta memoria quando ogni promessa deve allocare anche un paio di array (il benchmark crea promesse complessive di 80k, quindi 160k array non utilizzati assegnati). In realtà il 99,99% dei casi d'uso non promette mai più di una volta una promessa, quindi l'ottimizzazione per questo caso comune ottiene enormi miglioramenti nell'utilizzo della memoria.

Anche se V8 implementasse le stesse ottimizzazioni di bluebird, sarebbe comunque ostacolato dalle specifiche. Il benchmark deve usare new Promise(un anti-pattern in bluebird) in quanto non esiste altro modo per creare una promessa di root in ES6. new Promiseè un modo estremamente lento di creare una promessa, in primo luogo la funzione esecutore alloca una chiusura, in secondo luogo vengono passate 2 chiusure separate come argomenti. Sono 3 chiusure assegnate per promessa, ma una chiusura è già un oggetto più costoso di una promessa ottimizzata.

Bluebird può utilizzare promisifyche consente molte ottimizzazioni ed è un modo molto più conveniente di utilizzare API di callback e consente la conversione di interi moduli in moduli promettenti in una riga ( promisifyAll(require('redis'));).


10
"essere ancora ostacolato dalle specifiche" - Non sono sicuro di cosa significhi. Stai dicendo che ES6 sta seguendo una specifica intrinsecamente lenta, e se è così, significa che bluebird non sta seguendo la stessa specifica (e se è così, sta seguendo una diversa, e quale)? E c'è qualche motivo per cui ES6 non potrebbe avere un modo migliore di creare una Promessa di root oltre a new Promisemigliorare l'istanza per renderla meno costosa (come non creare 3 chiusure per istanza)?
Anthony

12
Non suona affatto bene (per JS). Non voglio davvero usare una libreria Promise quando c'è un'implementazione interna. Questa è una situazione più che sfortunata per tutti se questo è tutto vero. Ma ho già problemi a vedere l'hype di Promise, comunque, ho scritto 100.000 app LoC JS e ancora non vedo alcun reale bisogno per questo, è un miglioramento molto piccolo se non altro per me , principalmente nella gestione degli errori, no miglioramento nella gestione dei callback (non sono mai stato in "callback hell" con il mio stile di programmazione).
Mörre,

19
In ES6, non puoi usare Promise.resolve()per creare una "promessa di root"?
zetlen,

10
@ MörreNoseshine (continua) Anni dopo, gli autori di ES6 sono arrivati ​​e hanno detto "hey, specifichiamo che i motori JS devono fornire immediatamente un'utilità generica Promises / A + conforme, quindi le persone hanno sempre a disposizione uno strumento di base per le promesse ". Questa è una bella comodità (non dover importare una libreria solo per fare una rapida Promise.resolve()o altro), ma è un'implementazione molto semplice e la sua esistenza non dovrebbe scoraggiarti usando strumenti più seri legati alla promessa come bluebird!
callum,

11
@ MörreNoseshine 100k LOC App Javascript che probabilmente non ha mai avuto alcuna funzionalità asincrona. Buona fortuna a scrivere un gioco LoC JS da 100k con una libreria mysql / redis senza bluebird.
NiCk Newman,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.