Web server 'hello world' benchmark: Go vs. Node.js vs. Nim vs. Bun
lemire.me
lemire.me
Basically my understanding is:
- Bun uses JavaScript core and Node uses V8. They’re a bit different, no clear winner.
- Node is bloated for historical reasons, or has an extensive stdlib, depending on your perspective
- Bun uses uWebSockets which is a C++ built purpose built low-memory HTTP1.1 event loop based web server that is extremely fast (and reliable). Credit for speed should go to them, for http at least
- You can just use uWebSockets with Node if you want, it will be roughly equivalent
- Bun otoh does not support HTTP2 but “they’re working on it”, but nevertheless this is a huge feature blocker and depending on the implementation you may see major performance regressions (uWebSockets are not currently planning to implement http2 afaik, which means they have to use http2 as implemented by a mortal instead)
Overall, I think Bun should stop promoting microbenchmarks (but whatever, marketing is what it is) and focus on their real innovation value: a single-tooling simplified and modern server-side JS environment. This is already compelling. If they insist on benchmarking, why not build a standard crud app with Postgres/SQLite, put it behind nginx with ssl and compare there instead? The difference won’t be that big.
Biggest selling point of Bun is the potential it has to empty my node modules directory and free my mind of worries about supply chain attacks.
In my experience, even with a fast database like redis, one is unlikely to achieve more than like 5k req/sec per process in node.
However, as a server side runtime it doesn't matter much because the time spent on those libraries is minimal in compared to the time spent waiting for I/O
Fastify is not fast in Bun. Bun.serve() is 3x - 4x faster.
Our node:http code currently just wraps Bun.serve(). node:http has to go through many extra string -> buffer conversions and microticks and does a lot of unnecessary work. We have a branch that makes about 2x faster, but we need to prioritize our 1700+ open issues over performance optimizations right now.
Maybe “has incredibly high performance” should not be the first selling point in your marketing pitch then.
[1] and I'm not blaming them for that, their goal of being node compliant is a big undertaking
My company's frontend team (comprised mostly of people who believe anything written) jumped the wagon on Bun.
After a few days of working with it, it turned out that Bun isn't really faster in real world scenarios.
This brings up the question - why market it as so much faster then?
Many frameworks and tools market themselves primarily as faster than $otherThing but it won't fix suboptimal code, wrong choices for data structures, silly things like "let's select * and sort in browser". When you remove those mistakes that bad devs make, it turns out node/deno are more than fast enough. What we need is less bloat, more engineering and no lazy development by "packagemanager install lib-that-could-do-my-work"
Speed is somewhat the main selling point. It loses in most other metrics to node or deno. I think speed is a dumb thing to have as a main selling point too; nodejs/v8 are already quite fast, if I needed more speed I'd try to optimise it, before then rewriting it to rust. I want stability, soundness, features, a good toolchain etc.
Millions of voices suddenly cried out in terror, and were suddenly silenced.
Which scenarios? Are you sure it was using Bun and not Node? Is it using bun’s tools for things or the same tools as it did in node?
Recently I have even tried to port onde of my nextjs projects to it, but to my surprise it does not work on Linux ARM64 due to a broken syscall.
To me, the biggest selling point of Bun is not the runtime performance, but the fact that it can replace all tools for installing packages and bundling apps. (No need for a node modules for with hundreds of packages for a simple hello world web app)
Runtime performance is not that important for JS servers, considering that most of the applications are just http glue code to a database server. (Maybe it is just me, but I would never use JS to write performance critical code).
So focusing on making things stable would be a really good move right now.
In any case I'm super enthusiastic about Bun and it has a lot of potential. I hope you can make it work!
It's a fun exercise (I do it myself on occasion), but requires understanding what is actually happening (I see that Jarred has already remarked that Bun.serve() would be the right thing to do here, for instance.)
Technically true but very misleading. The linked article states that Uber switched to Go for 1 microservice that was doing a heavy amount of a specific mathematical processing that Go was better suited for.
Almost no difference for string manipulation little difference for floating-point math big perf difference for integer math
WASM vs JS also saw little differences in performance (except for integer math), in some cases it was slightly slower
There are several reasons to use a lower level language, but raw speed is hardly ever of note. It usually comes down to how good the libs you are using are. As Bun is showing even the stdlib of your runtime can be lackluster
I was specifically looking how big of an impact it is to use normal JS numbers for integer-heavy math. It turns out it is quite big, but for float64 math, not so much.
So the nim comparison is a bit of apples and oranges.
Also, the usual caveats of running micro benchmarks against localhost on an auto scaling CPU core...
I'm not sure why its number is quite low though.
Unsurprisingly there's a good number of C and C++ webserver repos. They're not all terrible - although some certainly are.
Would be interesting to see how the more usable ones compare.
It's relatively simple to write a trivial web server in C. To make a fast and usable web server in C is most likely anything but trivial.
You'll need:
- a way to register URL patterns and assign handler functions to them.
- a way to set shared mutable process-wide things such as connections to external services and make sure one thread doesn't trample on - or needlessly block - other thread's changes.
- a transparent way to make handlers work inside a single thread using asynchronous IO functions wherever possible. Fair enough - we are no longer in the trivial web server anymore.
anything eith a real workload is kinda irrelevant here, as this benchmark only focuses on hello world.
Likely, I can write one (http1.1) in Java just using java.nio + DirectBuffer; no converting anything to String (similar to using void* for everything)... in several hours. It'd even scale with the amount of cores the machines has. It won't do much but as a micro benchmark (hello world) would be up there.
That's nothing about usefulness of the code, or skill - it's just the benchmark is not useful for anything outside toying with.
https://acme.com/software/mini_httpd (slow) https://acme.com/software/thttpd (pretty decent)
A PHP, JS etc. runtime is always going to perform worse, the larger the codebase grows, than Go, Java, Rust etc.
A Bun/Node/Deno/Swoole/Workerman server that doesn’t actually run any substantial amount of JS/PHP (as is typical for benchmarks). Doesn’t actually tell you anything useful about the performance category your server can optimize towards.
These runtimes are obviously written in (almost exclusively) Zig, C++, Rust, C etc. which are languages that actually can express fast and lightweight code.
With a little bit of scripting on top of a server/runtime, this consideration doesn’t matter much. But any substantial amount of code is going to diverge very quickly if it’s not written in a compiled language.
I don't see how the size of your codebase could affect performance, maybe between GC languages and non-GC languages there could be a correlation. But between PHP/JS and Go/Java the size of your application shouldn't meaningfully affect performance.
Instead focus on tech decisions that enable you to ship what your paying customers want faster be it PHP, JavaScript, Node, Bun, Go…
The only part that makes some sense is Bun vs Nodejs, even if some important metrics are missing(response time, rate of errors).
I assume this is not consumer hardware. Is this something you'd normally find in a data center, eg would you rent this from AWS?
> The machine has 512 GB of RAM and 10x8 plus 2x4 TB SSDs, and the aforementioned dual Epyc 7543s for a whopping 128 logical cores.
Would like to see them against Microsoft's Kestrel as they have done a ton of work on that to try and make it quick as hell.
Edit: they “only” claim to be 5 times faster [1].
[1]: https://bun.sh/
Wait, wasn't that the point of bun in the first place?!
I'd be most interested to compare Node, Bun and Dino.
NodeJS seems to drop the ball on a handful of requests when it's getting slammed, i.e. upwards of 10s vs average of 100ms. Bun had a much smaller P50/P99 spread.
https://web-frameworks-benchmark.netlify.app/result
Community support is much more important than speed.