Skip to main content

Bun vs Node.js: A Load-Testing Experiment

· 4 min read
João Victor

Since its launch, Bun has emerged as a promising solution for developers seeking to optimize the performance of their JavaScript applications. Recently, there has been growing interest in utilizing the Bun runtime in production environments.

With this in mind, conducting a hands-on examination to assess the advantages of using Bun now seems good. Today, we'll use a straightforward load-testing experiment to compare the performance of an Express.js API running on two distinct JavaScript runtimes: Node.js and Bun.

Our analysis will center around the performance evaluation of an endpoint tasked with calculating Fibonacci numbers under load conditions.

Let's dive in!

The Code

You can access the code used for these tests via the following links:

The same codebase will be used for both runtime environments.

Configuring on zCloud

Next, let's proceed with the deployment of both APIs on zCloud.

Thankfully, configuring and deploying applications on zCloud is easy.

As illustrated in the image below, deploying an app simply entails selecting the desired computing resources, defining the app name, choosing the deployment region, and configuring Docker-related settings such as the Dockerfile path in your project.

configure-deploy-1.png

configure-deploy-2.png

With the setup complete, it's time to subject these endpoints to heavy load testing.

Firing

I'll utilize Autocannon, an HTTP benchmarking tool written in Node.js to run the load test. For our load test, we will use the following command:

npx -y autocannon -c 25 -w 4 -d 60 <URL>
  • The c flag determines the number of concurrent connections.
  • w specifies the number of worker threads.
  • Lastly, d sets the duration of the autocannon test in seconds.

This command will result in two tables containing data about the requests:

Bun: bun-autocannon-table.png

Node.js:

nodejs-autocannon-table.png

Now, let's compare the results.

Bun exhibited an average latency of 387.36 milliseconds, peaking at 3357 milliseconds. In contrast, Node.js recorded an average latency of 1112.88 milliseconds, with a peak of 3608 milliseconds.

The subsequent table focuses on request statistics. Bun successfully processed around 3,700 requests, achieving an average of 61 requests per second. Meanwhile, Node.js managed around 1,700 requests, averaging around 21 requests per second.

We can also compare the metrics using the zCloud Grafana dashboard. You can access the dashboard via the link available on the App Env page.

dashboards-dropdown.png

Comparison

Let's begin by evaluating the physical resources: CPU and memory.

While Node.js maxed out the CPU allocated for the app (0.5 zCloud), Bun reached only 60% of this value:

Bun:

cpu-usage-bun.png

Node.js:

cpu-usage-node.png

In terms of memory usage, Bun consumed nearly half the memory compared to Node.js:

Bun (~50MiB):

memory-usage-bun.png

Node.js (~80MiB):

memory-usage-node.png

Next, let's analyze the results of the requests in the Ingress Dashboard.

As illustrated in the graph below, Bun's API demonstrated the capability to transmit more than 20 KiB/sec of response, whereas Node.js peaked at just 8 KiB/sec:

ingress-reponse-size-edited.png

Additionally, the table below categorizes the request metrics by path and response status.

Bun:

ingress-table-bun.png

Node.js:

ingress-table-node.png

Bun handled almost triple as many requests as Node.js, with a lower average response time:

3704 / 1273 ≃ 2.9

Furthermore, regarding unsuccessful requests, Bun exhibited a failure rate of 0.4% with 499 error codes, whereas Node.js encountered a failure rate of 1.7%:

Bun:

(15 * 100) / (3704 + 15) ≃ 0.4

Node.js:

(22 * 100) / (1273 + 22) ≃ 1.7

Conclusion

In conclusion, Bun showcased superior performance metrics compared to Node.js, exhibiting higher request throughput, lower latency, and more efficient memory utilization.

It's crucial to emphasize that these findings are derived from benchmark testing of a primary endpoint. We are not advocating for the abandonment of Node.js, nor are we implying its inferiority. Instead, these results serve as objective data points for consideration.

Furthermore, zCloud's observability feature stands out as exceptional. It requires no additional setup, providing effortless and valuable application monitoring.

We didn't investigate why Bun performed much better in a CPU-bound task. If you know how to explain this difference, please publish more details.


Also watch the video in our YouTube channel covering everything that was said in the post.