maximedubourg.com

Benchmarking JS code, Golang flavored

27/01/2022

Unlike Golang, the JavaScript language (or more precisely, JavaScript runtimes) doesn't provide baked-in solutions for testing, nor benchmarking.

Here's how you would benchmark a function finding all the prime numbers under 1000 in Golang:

package main

import (
    "testing"
)

func BenchmarkFindPrimes(b *testing.B) {
    for i := 0; i < b.N; i++ {
        FindPrimes(1000)
    }
}

By running go test -bench=., you'd get something like that:

BenchmarkFindPrimes-8     14588     82798 ns/op

The key in Golang benchmark feature is b.N: as long as your for loop takes less than one second (default value) to complete, Go will keep (kinda) doubling the value of b.N and run the loop again. b.N will then take the values 1, 2, 4, 10, 20, 50, 100, 200...

So basically, for the prievous example:

JavaScript implementation

The implementation is pretty simple:

const { performance } = require("perf_hooks");

async function benchmark(fn) {
  let N = 0.5;
  let benchtime;

  do {
    N *= 2;

    let start = performance.now();
    await fn({ N });
    benchtime = performance.now() - start;
  } while (benchtime < 1_000);

  console.log({
    N,
    benchtime,
    nsOp: (benchtime * 1_000_000) / N,
  });
}

As explained before, we just double the value of N and measure again until the execution time becomes greater than one second.

Our benchmark function can be used as follows:

await benchmark((b) => {
  for (let i = 0; i < b.N; i++) {
    findPrimes(100);
  }
});

One important difference with Golang to be noted is that we must await every call to benchmark to be sure the output is representative: unlike Golang, JavaScript is a monothreaded language, which means not awaiting our benchmarks would make them run concurrently on a same thread and thus output biased results.

"Should I benchmark all my JS code ?"

No, you should'nt.

Most of your code probably doesn't even need benchmarking at all. Performance optimizations take time and often come with code readability tradeoffs, and thus should not be made before actually noticing performance issues.

But, in some cases like when developing critical web services or dev tooling, performance might actually be a need, and in such cases benchmarking small and isolated functions can help you maintaining you performance goals.