Performance metrics and benchmarking on Node.js
2 min read

Performance metrics and benchmarking on Node.js

Performance metrics and benchmarking on Node.js
Life before thinking about performance impact, memory and cpu

Lately, I've found myself worrying more and more about performance and the amount of time it takes for a function or a task to be taken on Node.js. Particularly, I wanted to benchmark and compare a particular code change to the original one. While researching for ways to implement and compare these functions, I came across Node.js's perf_hooks package which was added in the 8.5.0 release. For API definitions and use cases please refer to the documentation.

Performance Measurement

Let's first dive into a basic example of calculating the amount of time in milliseconds it takes to execute a specific function:

import { performance } from 'perf_hooks'

async function hardToSwallowPills() {
  performance.mark('start-benchmarking')
  await doSomeAsyncTask()
  performance.mark('end-benchmarking')
  performance.measure('hardToSwallowPills', 'start-benchmarking', 'end-benchmarking')
}

The important part you need to address in this code is, you need to manually mark a specific point in your codebase before and after the execution of it. After that, you need to measure the difference using performance.measure which takes the name/identifier of the measurement as the first parameter.

In order to log these measurements, you'll need the PerformanceObserver implementation from the perf_hooks.

import { PerformanceObserver } from 'perf_hooks'
const performanceObserver = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    logger
      .withTag('performance')
      .info(`${entry.name} took ${entry.duration.toFixed(2)} ms`)
  })
})
performanceObserver.observe({ entryTypes: ['measure'], buffered: true })

Benchmarking

In order to benchmark N number of implementations, you need to give the same input to both of them and compare them using the amount of time it takes to finish that task. For Node.js there's a really cool library called `benchmark` and provides a comparison and shows us the fastest implementation of our input.

Example implementation is:

import Benchmark from 'benchmark'
import { sort, randomArray } from './bubble.js'
import { sort: quickSort } from './quick.js'

const suite = new Benchmark.Suite()
const random = randomArray(100, 5000000)

suite
  .add('BubbleSort', () => sort(random))
  .add('QuickSort', () => quick(random))
  .on('cycle', function(event) {
    console.log(String(event.target))
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'))
  })
  .run({ 'async': true })

This will give you a brief result of your comparison like:

➜  algorithms node sorting/bubble.benchmark.js
BubbleSort x 5,721,443 ops/sec ±0.23% (96 runs sampled)