Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

I think your test is not correct.

Take a look at this code. I'll use benchmark for testing and I'll extract the variables and the closure creation to the top of the script so they are not created in every test run, only just one.

const Benchmark = require('benchmark');
const suite = new Benchmark.Suite();
const data = [];
for (let i = 0; i < 32000000; i++) {
  data.push(i * Math.random());
}
let sum = 0;
const adder = x => (sum += x * x);
const reducer = (acc, curr) => acc + curr * curr;
suite
  .add('standard for', function() {
    sum = 0;
    for (let i = 0; i < data.length; i++) {
      sum += data[i] * data[i];
    }
  })
  .add('for-of', function() {
    sum = 0;
    for (let datum of data) {
      sum += datum * datum;
    }
  })
  .add('forEach', function() {
    sum = 0;
    data.forEach(adder);
  })
  .add('reduce', function() {
    sum = data.reduce(reducer);
  })
  .on('cycle', function(event) {
    console.log(String(event.target));
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'));
  })
  .run({async: true});

The results are the following in  node v10.15.1

standard for x 0.68 ops/sec ±1.68% (6 runs sampled)
for-of       x 0.64 ops/sec ±1.01% (6 runs sampled)
forEach      x 0.66 ops/sec ±1.07% (6 runs sampled)
reduce       x 1.72 ops/sec ±0.41% (9 runs sampled)
Fastest is reduce

As you can see, there is almost no difference at all and the reduce version is clearly the fastest

(+1)

You've stepped into the trap that EricTboneJackson pointed out. By using sum inside a closure, write access to it is much slower. Because only one sum variable is shared across all your tests, this impacts all of them, despite not all of them requiring sum to be captured at all.

For comparison, I get these results on Node 12.2.0: 

standard for x 2.99 ops/sec ±0.42% (12 runs sampled)
for-of x 2.00 ops/sec ±0.50% (9 runs sampled)
forEach x 1.11 ops/sec ±0.77% (7 runs sampled)
reduce x 1.22 ops/sec ±0.28% (8 runs sampled)
Fastest is standard for

Then after performing these modifications:

  • capture sum only if necessary: remove the initial let sum declaration, then declare it inside the test itself (replace sum = 0 with let sum = 0)
  • move the closure declarations inside the test where they're needed (only strictly necessary for adder, since sum isn't declared yet)

The results are much more drastic.

standard for x 32.70 ops/sec ±0.25% (56 runs sampled)
for-of x 8.21 ops/sec ±0.33% (25 runs sampled)
forEach x 1.00 ops/sec ±0.73% (7 runs sampled)
reduce x 1.22 ops/sec ±0.11% (8 runs sampled)
Fastest is standard for

So, the main take-away should be: Where performance is important, avoid unnecessary closures.