Most use cases of the reduce() method can be easily rewritten with a for loop. And testing on JSPerf shows that reduce() is usually 60%-75% slower, depending on the operations performed inside each iteration.
Is there any real reason to use reduce() then, other than being able to write code in a 'functional style'? If you can have a 60% performance gain by writing just a little bit more code, why would you ever use reduce()?
EDIT: In fact, other functional methods like forEach() and map() all show similar performance, being at least 60% slower than simple for loops.
Here's a link to the JSPerf test (with function calls): forloop vs forEach
Most use cases of the reduce() method can be easily rewritten with a for loop. And testing on JSPerf shows that reduce() is usually 60%-75% slower, depending on the operations performed inside each iteration.
Is there any real reason to use reduce() then, other than being able to write code in a 'functional style'? If you can have a 60% performance gain by writing just a little bit more code, why would you ever use reduce()?
EDIT: In fact, other functional methods like forEach() and map() all show similar performance, being at least 60% slower than simple for loops.
Here's a link to the JSPerf test (with function calls): forloop vs forEach
Share Improve this question edited Mar 14, 2012 at 5:16 Evan You asked Mar 9, 2012 at 5:36 Evan YouEvan You 2,7111 gold badge26 silver badges15 bronze badges2 Answers
Reset to default 7The performance of the methods may vary depending on the size of the data.
Speed is also affected by compiler optimization and data warm-up.
Therefore on small data for of
wins, and on big reduce
insignificantly wins.
You can see for yourself by running the test:
const LOOP = 3
test(dataGenerator(5))
test(dataGenerator(500))
test(dataGenerator(50000))
test(dataGenerator(500000))
test(dataGenerator(5000000))
function test(dataSet) {
let sum
console.log('Data length:', dataSet.length)
for (let x = 0; x < LOOP; x++) {
sum = 0
console.time(`${x} reduce`)
sum = dataSet.reduce((s, d) => s += d.data, 0)
console.timeEnd(`${x} reduce`)
}
for (let x = 0; x < LOOP; x++) {
sum = 0
console.time(`${x} map`)
dataSet.map((i) => sum += i.data)
console.timeEnd(`${x} map`)
}
for (let x = 0; x < LOOP; x++) {
sum = 0
console.time(`${x} for loop`)
for (let i = 0; i < dataSet.length; i++) {
sum += dataSet[i].data
}
console.timeEnd(`${x} for loop`)
}
for (let x = 0; x < LOOP; x++) {
sum = 0
console.time(`${x} for reverse`)
for (let i = dataSet.length; i--;) {
sum += dataSet[i].data
}
console.timeEnd(`${x} for reverse`)
}
for (let x = 0; x < LOOP; x++) {
sum = 0
console.time(`${x} for of`)
for (const item of dataSet) {
sum += item.data
}
console.timeEnd(`${x} for of`)
}
for (let x = 0; x < LOOP; x++) {
sum = 0
console.time(`${x} for each`)
dataSet.forEach(element => {
sum += element.data
})
console.timeEnd(`${x} for each`)
}
console.log()
}
function dataGenerator(rows) {
const dataSet = []
for (let i = 0; i < rows; i++) {
dataSet.push({id: i, data: Math.floor(100 * Math.random())})
}
return dataSet
}
These are the results of a performance test on my laptop.
for loop
does not work stably unlike for reverse
and for of
.
➜ node reduce_vs_for.js
Data length: 5
0 reduce: 0.127ms
1 reduce: 0.008ms
2 reduce: 0.006ms
0 map: 0.036ms
1 map: 0.007ms
2 map: 0.018ms
0 for loop: 0.005ms
1 for loop: 0.014ms
2 for loop: 0.004ms
0 for reverse: 0.009ms
1 for reverse: 0.005ms
2 for reverse: 0.004ms
0 for of: 0.008ms
1 for of: 0.004ms
2 for of: 0.004ms
0 for each: 0.046ms
1 for each: 0.003ms
2 for each: 0.003ms
Data length: 500
0 reduce: 0.031ms
1 reduce: 0.027ms
2 reduce: 0.026ms
0 map: 0.039ms
1 map: 0.036ms
2 map: 0.033ms
0 for loop: 0.029ms
1 for loop: 0.028ms
2 for loop: 0.028ms
0 for reverse: 0.027ms
1 for reverse: 0.026ms
2 for reverse: 0.026ms
0 for of: 0.051ms
1 for of: 0.063ms
2 for of: 0.051ms
0 for each: 0.030ms
1 for each: 0.030ms
2 for each: 0.027ms
Data length: 50000
0 reduce: 1.986ms
1 reduce: 1.017ms
2 reduce: 1.017ms
0 map: 2.142ms
1 map: 1.352ms
2 map: 1.310ms
0 for loop: 2.407ms
1 for loop: 12.170ms
2 for loop: 0.246ms
0 for reverse: 0.226ms
1 for reverse: 0.225ms
2 for reverse: 0.223ms
0 for of: 0.217ms
1 for of: 0.213ms
2 for of: 0.215ms
0 for each: 0.391ms
1 for each: 0.409ms
2 for each: 1.020ms
Data length: 500000
0 reduce: 1.920ms
1 reduce: 1.837ms
2 reduce: 1.860ms
0 map: 13.140ms
1 map: 12.762ms
2 map: 14.584ms
0 for loop: 15.325ms
1 for loop: 2.295ms
2 for loop: 2.014ms
0 for reverse: 2.163ms
1 for reverse: 2.138ms
2 for reverse: 2.182ms
0 for of: 1.990ms
1 for of: 2.009ms
2 for of: 2.108ms
0 for each: 2.226ms
1 for each: 2.583ms
2 for each: 2.238ms
Data length: 5000000
0 reduce: 18.763ms
1 reduce: 17.155ms
2 reduce: 26.592ms
0 map: 145.415ms
1 map: 135.946ms
2 map: 144.325ms
0 for loop: 29.273ms
1 for loop: 28.365ms
2 for loop: 21.131ms
0 for reverse: 21.301ms
1 for reverse: 27.779ms
2 for reverse: 29.077ms
0 for of: 19.094ms
1 for of: 19.338ms
2 for of: 26.567ms
0 for each: 22.456ms
1 for each: 26.224ms
2 for each: 20.769ms
You might want scoping. For example you might want to make callback functions or have references to javascript objects. For more information, see why javascript is not blocked scoped.[edit: modern javascript now supportslet
variables. Back before ESv6, when you declared avar
variable, it got hoisted as if it was written at the top of the function codeblock, so often you had to write bodies of for-loops as functions. This following still applies though:] If you had a function written, might as well use functional style unless it's a significant bottleneck.- Your code does not always need to run at full machine speed. You may not even be optimizing code in the bottleneck.
- Additionally you do not provide your "testing on JSPerf" so we can critique it. For example if you already have a reduction function (or map or forEach function), then I bet the performance would be on-par. Even if not, the testing methodology may be flawed, especially given that many browsers may optimize differently or have different function-call overhead.
sidenote: this is a valid performance comparison between syntax, but an invalid performance comparison in when syntax is not the question at hand:
myArray.map(function(x){return x+1})
// ...versus...
for(var i=0; i<myArray.length; i++) {
myArray[i] = myArray[i]+1;
}
This would be a valid performance comparison:
myArray.forEach(function(x){return x+1})
// ...versus...
var plusOne = function(x){return x+1};
for(var i=0; i<myArray.length; i++) {
plusOne(myArray[i]);
}
// (may need a side-effect if the compiler is smart enough to optimize this)
(Also in reply to your edit: .forEach()
and .map()
provide much more clarity, and avoid the need for explicit loop int i=0; i<array.length; i++
arguments.)