Node.js vs Deno vs Bun: Hello World performance comparison

Mayank C
Tech Tonic

--

I’ve done a good number of performance comparisons in the past 2 years. All of those comparisons were good & valid at that time. However, things are changing fast these days. Promising new technologies are coming in (like Bun), and continual better performance is coming out (recent improvements in Deno and Node.js). Going with the fast changing world, the performance comparisons also need redoing to stay relevant. Any comparisons done a year back may be totally outdated now.

In this article, I’m going to compare Node.js, Deno, and Bun for the simplest hello world case. This is relooking at the performance with the recent releases in all the technologies. Let’s see if anything changes this time.

Test Setup

All the tests are executed on MacBook M1 with 16G RAM. The HTTP tester is the well known benchmarking tool called Bombardier. Bombardier is a HTTP(S) benchmarking tool. It is written in Go programming language and uses excellent fasthttp instead of Go’s default net/http library, because of its lightning fast performance. The tester runs the tests with the following levels of concurrency:

  • 10 concurrent connections
  • 50 concurrent connections
  • 100 concurrent connections
  • 300 concurrent connections

The software versions are:

  • Node.js v19.5.0
  • Deno v1.30.0
  • Bun v0.5.1

A total of 5M (5 million) requests are executed in each test.

Code

Only native HTTP servers are used in this comparison. No third-party packages or libraries are used.

The following is the hello world code in all the four systems:

Node.js

import http from "node:http";

http.createServer((_, resp) => {
resp.writeHead(200, {
"content-type": "text/plain",
});
resp.end("Hello world");
}).listen(3000);

Deno

Deno.serve((_) => new Response("Hello world!"), {
port: 3000,
});

Bun

Bun.serve({
port: 3000,
fetch(_) {
return new Response("Hello world!");
},
});

With the support of web APIs, the Deno and Bun code looks very similar. The request handler function is exactly the same. Node’s native HTTP server is still the old style callback based server.

Measurements

It’s not only about how fast a system is. A system/runtime can be very fast, but may stall the system by using too much CPU, memory, disk, etc. Some official benchmarks only focus on RPS. The more RPS, the better. Given unlimited resources, this is probably fine. However, for all practical purposes, only RPS doesn’t represent the complete picture. We need to measure latencies, and system usage too. Also, we need to see the response time distribution across different quantiles.

Here is the complete list of measurements:

  • 1st quartile (or 25th percentile) latency
  • Mean latency
  • Median latency
  • 3rd quartile (or 75th percentile) latency
  • 90th percentile latency
  • Maximum latency
  • Avg. CPU usage
  • Avg. Memory usage

Results

The following are the charts to show the results. There is one bar chart for each measurement metric. All concurrency levels are covered for each metric in the same chart.

All latencies are in microseconds

First, Node.js is very slow in comparison to Deno and Bun. The newer runtimes are between 2 to 3 times faster than the market leader, Node.js. Node.js offers about 75K RPS, while Deno goes around 180K and Bun goes around 195K. This is a huge difference.

Second, Bun and Deno’s performance is about the same. 180K vs 195K isn’t a big difference. Deno’s new flash server performs very well.

Finally, Bun’s excellent performance comes without hogging system resources. Bun’s CPU usage is the same as others, while the memory usage is very low.

--

--