A quick overview of Bun's basic features and how it compares to Node.js
Костя Третяк
Posted on August 28, 2024
Bun is a JavaScript runtime, positioned by its author as a set of ready-to-use tools "out of the box" provided along with the JavaScript runtime: this includes a package manager, a bundler, and a test runner. Additionally, the author strives to offer strong TypeScript support so that there’s no need to compile TypeScript files into JavaScript files. It's been almost a year since the first version of Bun was released. In this short post, I will examine how well the claimed features align with reality and the performance it delivers.
Installing Bun
As shown on Bun's main page, on Linux or macOS, it can be installed as follows:
curl -fsSL https://bun.sh/install | bash
It can be upgraded to the next stable version with:
bun upgrade --stable
Cloning the Repository with Benchmark Code
In this article, we will use the code that can be found in my repository. Clone it, navigate to the new directory, and install dependencies:
git clone https://github.com/KostyaTretyak/bun-vs-node.git
cd bun-vs-node
bun install
Additionally, install the wrk
benchmarking utility on your computer.
Comparing the Speed of Bun's Native Web Server and Node.js
Node.js on single process
First, let's see what Node.js shows, and then compare these results with Bun benchmarks. So, in the first terminal, run the node.mjs
file:
node node.mjs
On my machine, the cold start takes about 5ms.
Now, in the second terminal, run the benchmark:
wrk -H 'Connection: keep-alive' --latency -d 5 -c 256 --timeout 8 -t 4 http://localhost:3000/plaintext
This command makes requests in four threads, with a concurrency of 256, for a duration of 5 seconds. On my machine, the average result is as follows:
Running 5s test @ http://localhost:3000/plaintext
4 threads and 256 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 73.32ms 222.54ms 1.83s 92.72%
Req/Sec 4.50k 665.94 6.39k 76.50%
Latency Distribution
50% 13.74ms
75% 14.93ms
90% 79.47ms
99% 1.23s
89513 requests in 5.02s, 16.39MB read
Requests/sec: 17846.79
Transfer/sec: 3.27MB
So, about 17 thousand requests per second.
Node.js with cluster mode
Now let's try using the cluster mode. To do this, go back to the first terminal, stop the process by pressing Ctrl+C
, and run the following:
node node-cluster.mjs
On my machine, it has 4 cores, and each process in the cluster starts twice as slowly - about 10ms on average.
Now, run the benchmark again:
wrk -H 'Connection: keep-alive' --latency -d 5 -c 256 --timeout 8 -t 4 http://localhost:3000/plaintext
This time, the results are as follows:
Running 5s test @ http://localhost:3000/plaintext
4 threads and 256 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 9.03ms 18.69ms 345.62ms 97.60%
Req/Sec 9.26k 1.55k 12.23k 83.50%
Latency Distribution
50% 6.42ms
75% 8.32ms
90% 10.85ms
99% 85.68ms
184883 requests in 5.03s, 33.85MB read
Requests/sec: 36720.73
Transfer/sec: 6.72MB
So, 36 thousand requests per second, which is slightly more than twice as fast compared to running on a single core without cluster mode.
Bun on single process
Now let's run the same benchmarks with Bun. Go to the first terminal, press Ctrl+C
, and run:
bun bun.mjs
The process starts in about 4ms (almost as fast as in Node.js). When running the same benchmark with wrk
, the following result is obtained:
Running 5s test @ http://localhost:3000/plaintext
4 threads and 256 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 5.42ms 769.54us 26.37ms 93.00%
Req/Sec 11.87k 0.86k 12.95k 75.00%
Latency Distribution
50% 5.28ms
75% 5.80ms
90% 6.04ms
99% 6.77ms
236197 requests in 5.04s, 31.99MB read
Requests/sec: 46849.13
Transfer/sec: 6.34MB
So, Bun handles 46 thousand requests per second on a single core, which is 2.5 times faster compared to Node.js.
Bun with cluster mode
Again, go to the first terminal, press Ctrl+C
, and run:
bun bun-cluster.mjs
We see that in cluster mode, Bun's processes start just as quickly as without cluster mode.
Now, run the benchmark with wrk
again, and the following result is obtained:
Running 5s test @ http://localhost:3000/plaintext
4 threads and 256 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.09ms 2.08ms 32.22ms 70.02%
Req/Sec 19.97k 1.99k 27.94k 85.50%
Latency Distribution
50% 2.86ms
75% 4.16ms
90% 5.70ms
99% 9.41ms
399353 requests in 5.07s, 54.08MB read
Requests/sec: 78753.89
Transfer/sec: 10.66MB
So, 78 thousand requests per second. There is a performance increase, but only about 1.5 times compared to non-cluster mode. However, this is twice as fast as Node.js in cluster mode.
Describing a summary from my benchmarking experience, I can say that these are the best results comparing "Bun vs Node.js". It seems that Bun's web server is twice (or sometimes four times) faster, but this speed does not extend to all code. As you gradually increase the amount of JavaScript code in your application, Bun's performance will quickly degrade.
And when Bun works with database code, it shows noticeably worse results compared to Node.js. As far as I understand, this happens due to the significant amount of JavaScript code in database drivers.
How Bun Works with ExpressJS
Run it with the following command:
bun express.mjs
On my machine, Bun launches this code in about 10ms, while Node.js launches it in 4ms, meaning Node.js has a 2.5 times faster cold start.
However, Bun has a special feature designed to minimize startup time. To use it, create an executable file using the following command:
bun build ./express.mjs --compile --minify --outfile mycli
It will generate a mycli
file from express.mjs
, which can be run as follows:
./mycli
On my machine, this file launches slightly slower (about one millisecond slower) than the uncompiled file. However, this feature is primarily intended for large projects, so no optimization for cold start is noticeable in minimal code. However, the speed of the compiled file is slightly higher.
How Bun Works with Ditsmod
If you clone the seeds from Ditsmod and install the dependencies:
git clone --depth 1 https://github.com/ditsmod/seed.git my-app
cd my-app
bun install
And try to immediately run the application using Bun:
bun run build
bun dist/main.js
It will throw the following error:
1 | (function (entry, fetcher)
^
SyntaxError: export 'ValueProvider' not found in './types-and-models.js'
This bug is easily fixed - just delete all tsconfig files from node_modules/@ditsmod
:
rm node_modules/@ditsmod/*/tsconfig.json
That's it, now Bun works as expected:
bun dist/main.js
Summary
Personally, I don't yet see any significant advantages for switching to Bun. It has fast dependency installation, a fast web server, and possibly fast SQLite. If you open its repository, you can see that the number of bugs is overwhelming, so the Bun team has a hard time handling them.
If nothing significant happens in Bun's development over the next year, it will be unpromising for me.
Posted on August 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.