Node.js "for of", "forEach", "map" and "reduce" Overhead

This post compares the performance of the JavaScript for of, forEach, map and reduce with that of the basic for loop. It is firmly in the microbenchmark category and should be treated as such. Unless you are working with very large arrays any performance gains will be negligible.

Benchmark Results

Average time from 22 repetitions with the highest and lowest result removed:

1M elementsfor offorEachmapreduce
function3 ms12 ms12 ms12 ms
for loop3 ms3 ms7 ms3 ms
10M elements 
function33 ms148 ms116 ms117 ms
for loop26 ms26 ms70 ms26 ms
100M elements 
function378 ms2023 ms1230 ms1637 ms
for loop255 ms255 ms668 ms257 ms

Test environment

  • Node.js v18 LTS with -max-old-space-size=8192 argument
  • Ubuntu LTS 22.04
  • Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz

Observations

Arrays are fast. :)

Allocating 100 million objects caused a "JavaScript heap out of memory" error with default settings, requiring use of --max_old_space_size option.

In the for loop version of the map function, initialising with data.slice() proved the fastest. Initialising r with an empty array and using push() resulted in significant reduction in performance - so much so that it became similar to map(). Initialising with new Array(data.length) was initially as fast as using data.slice() faster except for 100M test where it became much slower than map() and consumed a lot more memory.

Running the tests with the --trace_gc option was interesting because it showed that the functional code was causing many more allocations. The for loop equivalent of reduce() did not trigger any garbage collection.

Node is generally getting faster with every version:

Change across node versions

Code Being Compared

Complete code available here.

for ... of

let r = 0;

for (const item of data) {
    r += item.value;
}

return r;
let r = 0;

for (let i = 0; i < data.length; i += 1) {
    r += data[i].value;
}

return r;

forEach

let r = 0;

data.forEach((item) => {
    r += item.value;
});

return r;
let r = 0;

for (let i = 0; i < data.length; i += 1) {
    r += data[i].value;
}

return r;

map

const r = data.map((item) => item.value);

return r.length;
const r = data.slice();

for (let i = 0; i < data.length; i += 1) {
    r[i] = data[i].value;
}

return r.length;

reduce

const r = data.reduce((i, item) => i + item.value, 0);

return r;
let r = 0;

for (let i = 0; i < data.length; i += 1) {
    r += data[i].value;
}

return r;