Who has the best response time for Hello world: Node.js, Deno, or Bun?

Mayank C
Tech Tonic

--

Ever since Bun came into the market (3–4 months back) and showed that it can do the exact same thing much faster, there has been a constant talk and a series of articles about who really is the fastest. Node.js, being the king of JS runtimes, doesn’t even bother about the other two. Deno and Bun practically don’t even exist from Node.js point of view. Deno has recently introduced a flash HTTP server that is way faster than the hyper based server. The competition is heating up. Though the fire has been started by Bun, Bun is still way too behind to even scratch Node’s universe.

In this article, I’ll compare Node.js, Deno, and Bun, to find out who gives the best average response time for the hello world HTTP server. The mean/average response time is calculated from the data collected by the tester. I’ll also have a look at the CPU and memory usage to find out the hidden cost of lower avg response time. The tests will be carried out for multiple concurrent connections, starting from 1 and reaching up to 200. Let’s get started.

The mean is the number you get by dividing the sum of a set of values by the number of values in the set. For a sample data of 2, 3, 3, 4, 6, 8, 9, to find the mean: add up all the values (2+3+3+4+6+8+9=35) and then divide that total by the number of values (7), resulting in a mean of 5.

The application

The best way to compare is to use the native servers. Both Bun and Deno have a very similar serve API that takes a web standard Request as input and expects a web standard Response as output. For Node.js, the native server is still the same with callback style.

Node.js

const http = require("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(req) {
return new Response("Hello world!");
},
});

The test environment

The tests are carried out on:

Both tester and SUT are local. The tester is a very minimal C++ code that uses std threads and libcurl for making HTTP requests.

The results

The target of all the tests is to run 5M requests to the server. For a sequential case, a single tester worker will execute all the 5M requests, while for 200 concurrent connections, each tester worker will execute 25K requests. The goal is to run a total of 5M requests.

Understanding the graphs: All the response time charts (bar graphs) plots average response time in milliseconds. The CPU chart shows % CPU used, while the memory chart shows usage in MBs. Lower is better for all the charts.

First, let’s have a look at the average response time & the cost (CPU, memory) for 1 concurrent connection:

Bun is faster than Node.js and Deno. For one-by-one requests, Bun offers an average 0.028ms response time, which is the least of the three. Bun has the lowest CPU usage, while Deno has the least memory usage of the three. As concurrency increases, the response time will increase too.

Next, let’s have a look at the same charts for 25 concurrent connections:

As expected, the avg. response time has increased several times once concurrency increases. Bun still leads with 0.20ms, while Node.js is the slowest at 0.28ms. It’s funny though because the difference is just about 0.08ms.

Next, let’s have a look at the same charts for 50 concurrent connections:

The gap widens at slightly higher concurrency. Bun’s avg response time is 0.44ms, Deno’s is 0.47ms, while Node’s is 0.59ms. Node continues to the slowest of all. Node’s memory usage also increases to about 80–90M. Deno’s memory usage is surprisingly constant at 42M. Bun tops at 160M. The CPU usage of Bun continues to be lowest of all.

Next, let’s have a look at the same charts for 100 concurrent connections:

The avg response time from Bun continues to be the lowest, though Node is just about 0.2ms slower than Bun. Deno’s avg response time is about the same as Bun.

Finally, let’s have a look at the same charts for 200 concurrent connections:

Nothing new. Bun leads with the least avg response time. Node trails with the maximum avg response time. Deno stays in between. The difference isn’t much, though. Node’s memory usage reaches about 100M, while Deno is rock steady at 42M.

Bonus, let’s do a quick check with 300 concurrent connections:

Conclusion

Purely looking at the response time numbers, Bun is the winner at all levels of concurrency. But the winning margin isn’t as much as it is advertised by Bun. Deno’s new flash server offers a great performance too. Node.js’s avg response time is always low when compared to Bun and Deno, but Node.js comes with a 12+ years of battle hardening experience that seems very hard to beat.

Now looking at system resources, Bun always uses the lowest CPU while Bun’s memory usage is quite high. Deno uses more CPU than Bun, but the lowest memory of all three. Node.js sits somewhere in between Bun and Deno. In fact, at all concurrency levels, Deno’s memory usage stays at around 42M.

Overall, at the time of writing, Deno seems to be the best bet. Again, this still doesn’t make a strong case against Node’s supremacy. Deno offers a pretty competitive avg response time with less CPU and very low memory usage.

--

--