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?