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 elements | for of | forEach | map | reduce |
---|---|---|---|---|
function | 3 ms | 12 ms | 12 ms | 12 ms |
for loop | 3 ms | 3 ms | 7 ms | 3 ms |
10M elements | ||||
function | 33 ms | 148 ms | 116 ms | 117 ms |
for loop | 26 ms | 26 ms | 70 ms | 26 ms |
100M elements | ||||
function | 378 ms | 2023 ms | 1230 ms | 1637 ms |
for loop | 255 ms | 255 ms | 668 ms | 257 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:
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;