Bluebird vs Native vs Async/Await - State of Promises performances in 2019

Recently we started working on Kuzzle v2.

This new major version will contain very few functional breaking changes but mostly updates of our dependencies, including the transition from Elasticsearch 5 to 7 and from Node.js 6 to 10.

 If you're looking for the 2020's results -> Click here

Version française

 

Concerning the transition from Elasticsearch 5 to 7, I will discuss this in detail in a future article.

 

About the transition to Node.js 10, the biggest question was what we were going to do about the current promises used in Kuzzle core. And especially if we finally went to use async/await in the whole core.

 

EDIT (12/2019): Kuzzle v2 is shipped with Node.js 12 by default so we started to use async/await in the core

Doxbee benchmark

For all our tests, we used the Doxbee benchmarks developed by the Bluebird team based on an article analyzing the different asynchronous patterns in Javascript.

 

These benchmarks simulate a situation in which requests are executed competitively with blocking read/write actions.

 

To reproduce them, you can clone this Github repository: https://github.com/petkaantonov/bluebird/

The benchmarking tools are in the benchmark/ folder.

 

TL;DR:

In Node.js 10, it is impossible to do without Bluebird in critical code areas given the performance of its promises compared to native promises and async/await.

 

In Node.js 12, the performance of async/await is comparable to that of Bluebird, so we can use async/await throughout Kuzzle core.

 

 

 

Node.js 6

Currently we use the Bluebird library for performance reasons. Indeed, Bluebird promises are up to 300% faster and consume 200% less memory than native Node.js 6 promises.

 

$ echo "./doxbee-sequential/promises-ecmascript6-native.js ./doxbee-sequential/promises-bluebird.js" | sed -e 's|\.js||' | xargs node ./performance.js --p 1 --t 1 --n 10000

file                        time(ms)  memory(MB)
promises-bluebird.js        227     45.75
promises-ecmascript6-native 945     145.09

Platform info:
Linux 4.10.0-38-generic x64
Node.JS 6.17.1
V8 5.1.281.111
Intel(R) Core(TM) i5-7300U CPU @ 2.60GHz × 4

Node.js 10

With this upgrade, we wanted to know if we would finally be able to do without Bluebird.

 

We did not find any recent benchmark results comparing the performance of Node.js 10 promises so we launched the benchmark with Node.js 10 ourselves:

 

echo "./doxbee-sequential/promises-native-async-await.js ./doxbee-sequential/promises-ecmascript6-native.js ./doxbee-sequential/promises-bluebird.js" | sed -e 's|\.js||' | xargs node ./performance.js --p 1 --t 1 --n 10000

file                            time(ms)  memory(MB)
promises-bluebird.js                 260       44.71
promises-native-async-await          322       68.64
promises-ecmascript6-native.js       332       73.80

Platform info:
Linux 4.10.0-38-generic x64
Node.JS 10.16.2
V8 6.8.275.32-node.54
Intel(R) Core(TM) i5-7300U CPU @ 2.60GHz × 4

 

We can see that even in Node.js 10, Bluebird promises are 20% faster than native promises and consume 40% less memory.

Similarly, Bluebird's performance exceeds that of promises managed with async/await.

 

So we decided to keep Bluebird to manage promises in critical code areas such as document writing and retrieval because these functions can be called several thousand times per second and any performance gain is good to take.

 

However, we will be able to use async/await in non-critical locations to simplify the readability and maintainability of Kuzzle code. In particular, we will be able to remove all the core generator functions.

Node.js 12

Out of curiosity, we also benchmarked the performance of the promises in Node.js 12. As a reminder, this new version of Node.js includes a significant performance gain on the management of promises with async/await. (See this excellent article on the V8 blog https://v8.dev/blog/fast-async)

 

echo "./doxbee-sequential/promises-native-async-await.js ./doxbee-sequential/promises-ecmascript6-native.js ./doxbee-sequential/promises-bluebird.js" | sed -e 's|\.js||' | xargs node ./performance.js --p 1 --t 1 --n 10000

file                            time(ms)  memory(MB)
promises-bluebird.js                 279       49.20
promises-native-async-await          280       53.22
promises-ecmascript6-native.js       318       65.82

Platform info:
Linux 4.10.0-38-generic x64
Node.JS 12.8.1
V8 7.5.288.22-node.16
Intel(R) Core(TM) i5-7300U CPU @ 2.60GHz × 4

 

We can see that the performance of promises managed with async/await is comparable to Bluebird's promises!

 

Node.js 13

 

Unfortunately the LTS of Node.js 12 is released in October and we absolutely need to release Kuzzle v2 beforehand to compensate for the end of the official support Elasticsearch 5 and Node.js 6.

 

On the other hand, we will not hesitate to replace all the Bluebird promises of Kuzzle's core with async/await promises for Kuzzle v3;)

 

EDIT (12/2019): Since we finally defer the release, we were able to use Node.js 12 by default in Kuzzle v2 so we incrementally replace Bluebird promises by async/await promises.

 

 

Alexandre Bouthinon

Related posts