Pourquoi les promesses de l'ES6 natif sont-elles plus lentes et utilisent plus de mémoire que Bluebird?

195

Dans cet exemple , la suite prend 4 fois plus de temps avec les promesses ES6 par rapport aux promesses de Bluebird et utilise 3,6 fois plus de mémoire.

Comment une bibliothèque JavaScript peut-elle être tellement plus rapide et plus légère que l'implémentation native de v8 écrite en C? Les promesses Bluebird ont exactement la même API que les promesses natives ES6 (plus un tas de méthodes utilitaires supplémentaires).

L'implémentation native est-elle simplement mal écrite ou y a-t-il un autre aspect qui me manque?

callum
la source
N'oubliez pas que les implémentations JavaScript modernes sont fortement optimisées et peuvent même fonctionner de manière native avec JIT .
1
Selon This Benchmark , BlueBirdJS est en réalité plus lent que Native Promises. Mais PromiseMeSpeedJS les dépasse tous les deux. Une des nombreuses choses que PromiseMeSpeedJS prouve à travers cela est qu'un coupable majeur dans l'exécution de promesses est la surutilisation abusive de l' newopérateur parce que PromiseMeSpeedJS ne l'utilise pas new.
Jack Giffin
1
@JackGiffin Chrome 67: PromiseMeSpeedJS ralentit de 46%, Bluebird de 61%.
FINDarkside

Réponses:

272

Bluebird auteur ici.

L'implémentation des promesses V8 est écrite en JavaScript et non en C. Tout le JavaScript (y compris le propre de V8) est compilé en code natif. De plus, le code JavaScript écrit par l'utilisateur est optimisé, si possible (et vaut la peine), avant d'être compilé en code natif. La mise en œuvre des promesses ne bénéficierait pas beaucoup ou pas du tout d’être écrite en C, elle ne le ralentirait en fait que parce que tout ce que vous faites est de manipuler des objets JavaScript et de communiquer.

L'implémentation V8 n'est tout simplement pas aussi optimisée que bluebird, elle alloue des instances pour les gestionnaires de promesses . Cela prend beaucoup de mémoire lorsque chaque promesse doit également allouer deux tableaux (le point de référence crée des promesses globales de 80 000 donc 160 000 tableaux inutilisés alloués). En réalité, 99,99% des cas d'utilisation ne s'emboîtent jamais plus d'une fois dans la promesse. L'optimisation de ce cas courant permet ainsi d'améliorer considérablement l'utilisation de la mémoire.

Même si V8 implémentait les mêmes optimisations que bluebird, la spécification l’empêcherait. Le benchmark doit être utilisé new Promise(un anti-motif dans bluebird) car il n’ya pas d’autre moyen de créer une promesse fondamentale dans ES6. new Promiseest une manière extrêmement lente de créer une promesse, d’abord la fonction exécuteur alloue une fermeture, d’autre part, 2 fermetures séparées lui sont transmises comme arguments. C'est 3 fermetures allouées par promesse, mais une fermeture est déjà un objet plus coûteux qu'une promesse optimisée.

Bluebird peut utiliser promisifyce qui permet de nombreuses optimisations et constitue un moyen beaucoup plus pratique de consommer des API de rappel et permet la conversion de modules entiers en modules à base de promesses en une seule ligne ( promisifyAll(require('redis'));).

Esailija
la source
10
"toujours être gêné par la spécification" - Vous ne savez pas ce que cela signifie. Êtes-vous en train de dire que ES6 suit une spécification qui est intrinsèquement lente, et si tel est le cas, cela signifie-t-il que bluebird ne suit pas la même spécification (et si tel est le cas, est-ce qu'elle en suit une autre et laquelle?)? Et y at-il une raison pour laquelle ES6 ne pourrait pas avoir un meilleur moyen de créer une promesse racine new Promiseou d’améliorer l’instanciation pour la rendre moins coûteuse (comme ne pas créer 3 fermetures par instance)?
Anthony
12
Cela ne sonne pas bien du tout (pour JS). Je ne souhaite vraiment pas utiliser une bibliothèque Promise lorsqu'il y a une implémentation interne. C'est une situation plus que malheureuse pour tout le monde, si tout est vrai. Mais j’ai déjà du mal à voir le battage publicitaire de Promise, j’ai écrit 100 000 applications LoC JS et je ne vois toujours pas un réel besoin pour cela, c’est une très légère amélioration, voire aucune, pour moi , surtout en ce qui concerne la gestion des erreurs, non. amélioration de la gestion des rappels (je n'ai jamais été dans «enfer de rappel» avec mon style de codage).
Mörre
19
Dans ES6, ne pouvez-vous pas utiliser Promise.resolve()pour créer une "promesse racine"?
Zetlen
10
@ MörreNoseshine (suite) Des années plus tard, les auteurs de l'ES6 sont arrivés et ont déclaré: "Hé, précisons que les moteurs JS doivent fournir un utilitaire prêt à l'emploi générique conforme à Promises / A +, afin que les utilisateurs disposent toujours d'un outil de promesse de base ". C'est une bonne commodité (ne pas avoir à importer une bibliothèque pour faire un rapide Promise.resolve()ou quoi que ce soit), mais c'est une implémentation très basique, et son existence ne devrait pas vous décourager d'utiliser des outils plus sérieux liés aux promesses comme bluebird!
callum
11
@ MörreNoseshine 100k application Javascript de LOC qui n'a probablement jamais eu aucune fonctionnalité asynchrone. Bonne chance pour écrire un jeu de 100k LoC JS avec une bibliothèque mysql / redis sans bluebird.
NiCk Newman