JavaScript's forEach is very slow!
Edit: as has been pointed out in the comments below by epidemian this only effects JavaScript ran using node. The same test in a browser gives much more expected results. Thanks epidemian!
Edit2: the performance of for of loops and for each is now much more equivalent in newer versions of node. But the standard for loop is still significantly faster.
I'm currently drinking fairly heavily from the function programming Kool-Aid and have been using it extensively in new projects. I've found that a lot of inner logic can be very elegantly written in a function programming style.
Most of my game projects are written for the web using JavaScript (or more specifically TypeScript). However I was recently made aware of how much slower many of JavaScript's functional style routines are compared to their non-functional counterparts. It saddened me.
Benchmarking
As an example lets compute the sum of all the elements in an array squared.
We will initialise the array as follow:
const data = [] for(let i = 0; i < 32000000; i++){ data.push(i * Math.random()) }
We will then compare:
- standard for loop
- for of loop
- forEach
- reduce
I am using Node v10.15.3.
Standard for loop
console.time('for') let sum = 0 for(let i = 0; i < data.length; i++){ sum += data[i] * data[i] } console.timeEnd('for')
This clocks in at ~35ms.
For of loop
console.time('for of') let sum = 0 for(let datum of data){ sum += datum * datum } console.timeEnd('for of')
This clocks in at ~520ms.
ForEach
console.time('forEach') let sum = 0 data.forEach(x => { sum += x * x }) console.timeEnd('forEach')
This clocks in at ~1200ms.
Reduce
console.time('reduce') let sum = data.reduce((acc, curr) => { return acc + curr * curr }) console.timeEnd('reduce')
This clocks in at ~600ms.
Overview
I've plotted the results below as a summary.
I found this very surprising.
I typically argue for a simpler, more readable programming style over eking out every last bit of performance for performance's sake. However these results have really made me stop and think about using this more functional style when making games in JavaScript. Which is a shame. It's really hard to argue for a nicer coding style when its around thirty times slower. Especially if it's a style widely adopted throughout a codebase.
It's worth noting that many uses of JavaScript aren't looping over large arrays and processing lots of maths. They typically handle button presses and AJAX calls etc. In these cases it isn't really an issue and the nicer coding style wins. However, for games, this is very common i.e. for all the objects in the world etc.
It's also worth noting that this is very much a micro benchmark. So the effect may be lessened in more real world use cases. It would be interesting to investigate this.
I'm hoping that updates to the JavaScript interpreter might, over time, bring down the overhead of using the more functional style. They've done a fairly amazing job making the standard for loop as fast as it is!
Anyway, thanks for reading. I hope some of you found this interesting / useful.
Turnaround Games