0

Sometimes in node.js (0.x - 6.x) we've seen that you can speed up CPU bound work by artificially making the processing asynchronous. Also, it seems that sometimes adding a setTimeout on the asynchronous callback makes it even faster. The CPU bound processing has varied in our system but oddly we've sometimes seen speedups of 5-6x by just making the function async or async+setImmediate.

Here's a quick program that I threw together that illustrates this behavior. The program iterates through an array and computes a bar variable.

'use strict'
const async = require('async');
const moment = require('moment');

const iterations = 1000;

let createArray = ()=>{
    let result = [];
    for (var i =0; i < iterations; i++){
        result.push({
            foo: i
        });
    }
    return result;
}

let synchronousForLoop = (a)=>{
    for (var i =0; i < a.length; i++){
        a[i].bar = a[i].foo + 1;
    }
};

let synchronousForEachLoop = (a)=>{
    a.forEach((item)=>{
        item.bar = item.foo + 1;
    });
};

let asyncWithOutSetImmediate = (a, callback)=>{
    async.eachLimit(a, 1, (item, next)=>{
        item.bar = item.foo + 1;
        next();
    }, callback);
};

let asyncWithSetImmediate = (a, callback) =>{
    async.eachLimit(a, 1, (item, next)=>{
        item.bar = item.foo + 1;
        async.setImmediate(next);
    }, callback);
};

async.series([
    (seriesCallback)=>{
        let a = createArray();
        let start = moment.utc();
        synchronousForLoop(a);
        console.log('synchronousForLoop time taken: '+moment.utc().diff(start));
        seriesCallback();
    },
    (seriesCallback)=>{
        let a = createArray();
        let start = moment.utc();
        synchronousForEachLoop(a);
        console.log('synchronousForEachLoop time taken: '+moment.utc().diff(start));
        seriesCallback();
    },
    (seriesCallback)=>{
        let a = createArray();
        let start = moment.utc();
        asyncWithOutSetImmediate(a, ()=>{
            console.log('asyncWithOutSetImmediate time taken: '+moment.utc().diff(start));
            seriesCallback();
        });
    },
    (seriesCallback)=>{
        let a = createArray();
        let start = moment.utc();
        asyncWithSetImmediate(a, ()=>{
            console.log('asyncWithSetImmediate time taken: '+moment.utc().diff(start));
           seriesCallback();
        });
    }
], (err)=>{
    console.log('done');
});

Here's the console output:

syncronousForLoop time taken: 7
syncronousForEachLoop time taken: 1
asyncWithOutSetImmediate time taken: 1
asyncWithSetImmediate time taken: 5
done

This seems counter to what you'd think should be the results. Why wouldn't the for loop be the fastest? Why is the forEach loop faster than the for loop? Why are the asynchronous functions faster than the for loop? Why in some cases, not this one, is the asynchronous version with setImmediate use the fastest?

Zambonilli
  • 4,358
  • 1
  • 18
  • 18
  • You should be iterating the benchmarks and measuring with [`process.hrtime`](https://nodejs.org/api/process.html#process_process_hrtime_time) instead of `moment.utc`. And separating the runs so they don’t affect each other’s optimization. – Ry- Oct 10 '17 at 03:14
  • Related: [Asynchronous vs synchronous execution, what does it really mean?](https://stackoverflow.com/questions/748175/asynchronous-vs-synchronous-execution-what-does-it-really-mean) – Ram Oct 10 '17 at 03:14
  • 1
    Asynchronous operation can never be faster than its synchronous counterpart, simply because it does exactly the same amount of work + has the asynchronicity overhead. – zerkms Oct 10 '17 at 03:16
  • @zerkms Didn't you miss the _looping_ part? That's true just for one operation. – Ram Oct 10 '17 at 03:19
  • 1
    @Ram for CPU bound code it's always true. If one gets results that don't comply with it - the benchmark is totally broken. – zerkms Oct 10 '17 at 03:30
  • @Ryan I switched the measurements from moment to process.htime and I'm seeing synchronous faster than asynchronous. If you add an answer saying as much, I'll mark it as correct. Thank you – Zambonilli Oct 10 '17 at 13:09

0 Answers0