Bun.JS Is Indeed Faster

I have a dirty secret - I use Bun instead of NodeJS for my own projects. I use it because of a better developer experience and other good features. I always take someone’s claims about better performance with a grain of salt. The benchmarks and any other comparations are as much a marketing tool as anything else, especially when you don’t have time to check those claims yourself. In my case I had a chance to compare Bun performace myself and I can confirm - Bun is faster than NodeJS.

If you did more or less JavaScript you probably know that the Garbage Collector is one of the top reasons of hight CPU usage and it contibutes a great deal in application performace downgrade. It is especcially true for any high loaded services, and this is usually the main reason why engineers sometimes resort to rewriting their work in other languages. This is not in particular a “javascript” problem, any garbage collected language has its own patterns to mitigate GC spikes.

Recently I had to write a test for my pet project in development DAL (Data Access Layer) to check for possible memory leaks. DAL is a proxy for relational databases and it is written in Go, which is also a garbage collected language. Initially it was written in NodeJS, but some time ago I’ve decided to refresh the codebase and rewrite it in Go for various reasons. And for the sake of backward compatibility with older versions I have also decided to explore the ways to integrate DAL into nodejs process, and because I use Bun, I couldn’t miss the oppotunity to check their FFI interface.

Marrying Golang and JS wasn’t a challenge. A simple C++ glue in NodeJS case, and FFI/dlopen in Bun’s case. The main thing I am watching for is possible memory leaks. It is easy to miss some pointer during development and then the whole thing is destined to crash.

Testing Methology

In order to check for the memleaks I wrote a benchmarks that spams my library with a 100M (100 000 000) simple queries which is about 10Gb of data transfer. The benchmark tracks process’ memory, and if we have a leak - the difference between RAM usage on starutup and at the end of execution would be great. The benchmark also gives an oppotunity to check how well GC is performing under the load.

OSMac OS, ARM64
DeviceMacBook Air M2
RAM24Gb
DBSQLite 3, WAL mode
NodeJSv20.10.0
Bunv1.1.25

Following query is encoded to message pack format and passed to the builder method using native bindings:

instance
    .In('table')
    .Find({
        a: 1,
        b: {
            $gt: 2,
        },
    })
// SELECT * FROM table WHERE a = 1 AND b > 2  ;

The database responds with an error message, so we don’t read anything from the disk. The test’s purpose is to check for the leaks and measure performace.

Metrics

  • Process memory at Start
  • Process memory at N iteration
  • Average memory at the End
  • Time to end

Full benchmark source can be found here.

bun bench:node #npm run bench:node
bun bench:bun

The main differences

  • Bun opens the shared library (dylib) and allocates it in the extenal memory.
  • NodeJS uses NAPI - C++ binding is slightly diiferent but uses the same methods.
  • Both implementations work the same way and free unused memory manually when required.
  • Bun did not require to write C++ glue, it provides an FFI interface.

NodeJS Performance

START
  rss: 32 Mb
  external: 1 Mb
  buffers: 3 Mb
  total: 4 Mb

Data transfered:  11539 Mb
Time to end: 1:16.586 (m:ss.mmm)

AVERAGE:
  rss: 51 Mb
  external: 2 Mb
  buffers: 4 Mb
  total: 6 Mb

Observations:

  • High CPU usage (top - ~120%)

Bun Performance

START
  rss: 37 Mb 
  external: 0 Mb 
  buffers: 0 Mb 
  total: 2 Mb 

Data transfered:  11539 Mb
[46.25s] Time to end

AVERAGE:
  rss: 82 Mb
  external: 25 Mb
  buffers: 29 Mb
  total: 22 Mb

Observations:

  • High CPU usage (top - ~101%)

The Result

RuntimeRSS STARTAverage Memory at RuntimeTime To End
NodeJS32Mb51Mb116sec
Bun37Mb82Mb46sec

Even though Bun allocated more memory at runtime, the execution time of the same script was 2.5 x faster than in NodeJS (46sec <> 116sec). For myself, I’ve concluded that Bun might be cheaper in terms of the cloud costs.