bumblehead.com{JSON:Feed}
  • blog
  • media
  • links
  • about
newerwhat's faster

callbacks win

  • bumblehead
  • blog
  • 2024-09-22

Javascript callbacks are faster than async-await.

This benchmark can be used with node's official benchmark.js scripts The last number is the rate of operations measured in ops/sec (higher is better).

async-vs-cb.nested-benchmark.js
const common = require('./node/benchmark/common.js');

const bench = common.createBenchmark(main, {
  n: [ 1400 ],
  type: [ 'await-deep', 'await-shallow', 'cb-deep', 'cb-deep-promisified' ]
});

async function main(conf) {
  let res = Math.random()

  const string = conf.string
  const type = conf.type

  if (type === 'await-deep') {
    bench.start();
    res = await (async function nestedAsync (val, count) {
      if (!count--) return val

      return nestedAsync(Math.random() > Math.random() ? 1 : -1, count)
    })(Math.random(), conf.n)
    bench.end(conf.n);
  }

  if (type === 'await-shallow') {
    const arr = Array.from({length: conf.n}, (v, i) => i)
    const oneAsyncRes = async (val, count) => (
      count + Math.random() > Math.random() ? 1 : -1)
    
    bench.start();
    for (const n of arr)
      res = await oneAsyncRes(n, res)
    bench.end(conf.n);
  }

  if (type === 'cb-deep') {
    bench.start();
    (function nestedCb(val, count, cb) {
      if (!count--) return cb(null, val)

      return nestedCb(Math.random() > Math.random() ? 1 : -1, count, cb)
    })(Math.random(), conf.n, (err, res) => {
      bench.end(conf.n)
    })
  }

  if (type === 'cb-deep-promisified') {
    bench.start();
    const res = await new Promise(resolve => (
      (function nestedCb(val, count, cb) {
        if (!count--) return cb(null, val)

        return nestedCb(Math.random() > Math.random() ? 1 : -1, count, cb)
      })(Math.random(), conf.n, (err, res) => {
        resolve(res)
      })
    ))
    bench.end(conf.n)
  }

  return res
}
$ node async-vs-cb.nested-benchmark.js
.js type="await-deep"          n=1400: 1,516,848.9413477853
.js type="await-shallow"       n=1400: 1,137,102.9378678831
.js type="cb-deep"             n=1400: 2,214,625.7045278023
.js type="cb-deep-promisified" n=1400: 1,907,328.3642888186

Conclusion: performance-sensitive, deeply-stacked callback code should not be converted to async-await.


also updating benchmarks to send and receive destructured values shows significant performance drop, so that async/await with array destructuring is very slow only getting about ~200,000 ops/sec on this local machine.

To elaborate, callback functions may receive multiple values, for example,

callback(function (a, b, c, d) {
  // ...
})

async function callers must receive one value and separate values are found with lookups or slower destructuring,

[a, b, c, b] = await asyncfn();

sending and receiving destructured values drops performance significantly, so that async/await with array destructuring is very slow only getting about ~200,000-300,000 ops/sec on the local machine here.

Callback parens

tags
  • software
older
Big Rewrite Idea
  • bumblehead
  • 2017-07-17
newer
what's faster
  • bumblehead
  • 2024-10-12

© bumblehead

0.2.0site-map