Skip to content

Instantly share code, notes, and snippets.

@KillyMXI
Created April 25, 2021 15:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KillyMXI/b525066500ad646d34ce004408f9d400 to your computer and use it in GitHub Desktop.
Save KillyMXI/b525066500ad646d34ce004408f9d400 to your computer and use it in GitHub Desktop.
Node.js map/for performance benchmark comparison

Comparison of different way to map an array in Node.js

  • Properly used map is quite good;

  • Badly used for_i can be absolutely horrible;

  • A custom map-like function is on par with bare for_i;

  • Not all for_i loops are equally optimized;

  • Node 10, 12 and 14 have the same overall picture;

  • Arrow vs function callbacks - makes no significant difference.

Takeaway:

  1. map is the way to go most of the times, arrow or function callback - doesn't matter;
  2. No need to write bare for_i loops everywhere in the hot path - wrapper map-like function can be indistinguishable in performance;
  3. Don't take anything for granted - test it. Not all for_i loops are equal.
// npm i benny
const b = require('benny');
function preallocCopy1 (src) {
const res = new Array(src.length);
for(var i=src.length; i-- > 0;) {
res[i] = src[i];
}
return res;
}
function preallocMap1 (f, src) {
const res = new Array(src.length);
for(var i=src.length; i-- > 0;) {
res[i] = f(src[i]);
}
return res;
}
function preallocCopy2 (src) {
const res = new Array(src.length);
for(var i=src.length; i > 0; i--) {
res[i] = src[i];
}
return res;
}
function preallocMap2 (f, src) {
const res = new Array(src.length);
for(var i=src.length; i > 0; i--) {
res[i] = f(src[i]);
}
return res;
}
function preallocCopy3 (src) {
const len = src.length;
const res = new Array(len);
for(var i=0; i++ < len;) {
res[i] = src[i];
}
return res;
}
function preallocMap3 (f, src) {
const len = src.length;
const res = new Array(len);
for(var i=0; i++ < len;) {
res[i] = f(src[i]);
}
return res;
}
function preallocCopy4 (src) {
const len = src.length;
const res = new Array(len);
for(var i=0; i < len; i++) {
res[i] = src[i];
}
return res;
}
function preallocMap4 (f, src) {
const len = src.length;
const res = new Array(len);
for(var i=0; i < len; i++) {
res[i] = f(src[i]);
}
return res;
}
const input = 'giyrxgfoyugrxeupqzuehfgfccsydgfpxwieuhqzuiehfuiegfyuegfpegrygrypiop634927chmiuqefdyiqdetfcityrfiwecfiuegciquegiyrxgfoyugrxeupqzuehfgfccsydgfpxwieuhqzuiehfuiegfyuegfpegrygrypiop634927chmiuqefdyiqdetfcityrfiwecfiuegciquegiyrxgfoyugrxeupqzuehfgfccsydgfpxwieuhqz'.split('');
b.suite(
'map_for_etc',
b.add('map(function ~ id)', () => {
var output = input.map(function (x){ return x; });
}),
b.add('map(arrow ~ id)', () => {
var output = input.map((x) => x);
}),
b.add('map(function ~ push)', () => {
var output = [];
input.map(function (x){ output.push(x); });
}),
b.add('map(arrow ~ push)', () => {
var output = [];
input.map((x) => output.push(x));
}),
b.add('forEach(function ~ push)', () => {
var output = [];
input.forEach(function (x){ output.push(x); });
}),
b.add('forEach(arrow ~ push)', () => {
var output = [];
input.forEach((x) => output.push(x));
}),
b.add('for_of(push)', () => {
var output = [];
for(const x of input) { output.push(x); }
}),
b.add('for_i1(push ~ backwards, combined)', () => {
var output = [];
for(let i = input.length; i-- > 0;) { output.push(input[i]); }
}),
b.add('for_i2(push ~ backwards)', () => {
var output = [];
for(let i = input.length; i > 0; i--) { output.push(input[i]); }
}),
b.add('for_i3(push ~ forward, combined)', () => {
const len = input.length;
var output = [];
for(let i = 0; i++ < len;) { output.push(input[i]); }
}),
b.add('for_i4(push ~ forward)', () => {
const len = input.length;
var output = [];
for(let i = 0; i < len; i++) { output.push(input[i]); }
}),
b.add('for_i1(preallocate ~ backwards, combined)', () => {
const len = input.length;
var output = new Array(len);
for(let i = len; i-- > 0;) { output[i] = input[i]; }
}),
b.add('for_i2(preallocate ~ backwards)', () => {
const len = input.length;
var output = new Array(len);
for(let i = len; i > 0; i--) { output[i] = input[i]; }
}),
b.add('for_i3(preallocate ~ forward, combined)', () => {
const len = input.length;
var output = new Array(len);
for(let i = 0; i++ < len;) { output[i] = input[i]; }
}),
b.add('for_i4(preallocate ~ forward)', () => {
const len = input.length;
var output = new Array(len);
for(let i = 0; i < len; i++) { output[i] = input[i]; }
}),
b.add('preallocCopy1( ~ backwards, combined)', () => {
var output = preallocCopy1(input);
}),
b.add('preallocCopy2( ~ backwards)', () => {
var output = preallocCopy2(input);
}),
b.add('preallocCopy3( ~ forward, combined)', () => {
var output = preallocCopy3(input);
}),
b.add('preallocCopy4( ~ forward)', () => {
var output = preallocCopy4(input);
}),
b.add('preallocMap1(id ~ backwards, combined)', () => {
var output = preallocMap1((x) => x, input);
}),
b.add('preallocMap2(id ~ backwards)', () => {
var output = preallocMap2((x) => x, input);
}),
b.add('preallocMap3(id ~ forward, combined)', () => {
var output = preallocMap3((x) => x, input);
}),
b.add('preallocMap4(id ~ forward)', () => {
var output = preallocMap4((x) => x, input);
}),
b.cycle(),
b.complete(),
b.save({ file: 'map_for_etc', format: 'json' }),
b.save({ file: 'map_for_etc', format: 'csv' }),
b.save({ file: 'map_for_etc', format: 'chart.html' }),
);
{
"name": "map-for-benchmark",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "node ./index.js"
},
"devDependencies": {
"benny": "^3.6.15"
}
}
name ops margin percentSlower
map(function ~ id) 791400 13.71 41.59
map(arrow ~ id) 794583 4.93 41.36
map(function ~ push) 324214 13.91 76.07
map(arrow ~ push) 361015 3.76 73.36
forEach(function ~ push) 481734 0.56 64.45
forEach(arrow ~ push) 483360 0.74 64.33
for_of(push) 435232 3.67 67.88
for_i1(push ~ backwards, combined) 472390 0.68 65.14
for_i2(push ~ backwards) 196597 1.71 85.49
for_i3(push ~ forward, combined) 194609 0.72 85.64
for_i4(push ~ forward) 469562 3.47 65.35
for_i1(preallocate ~ backwards, combined) 1318294 0.92 2.71
for_i2(preallocate ~ backwards) 572045 0.79 57.78
for_i3(preallocate ~ forward, combined) 552953 3.72 59.19
for_i4(preallocate ~ forward) 1355011 0.92 0
preallocCopy1( ~ backwards, combined) 1302593 1.13 3.87
preallocCopy2( ~ backwards) 561530 3.81 58.56
preallocCopy3( ~ forward, combined) 561234 1.19 58.58
preallocCopy4( ~ forward) 1328549 4.45 1.95
preallocMap1(id ~ backwards, combined) 1317444 0.67 2.77
preallocMap2(id ~ backwards) 557745 0.66 58.84
preallocMap3(id ~ forward, combined) 546837 3.79 59.64
preallocMap4(id ~ forward) 1352058 1.54 0.22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment