for loop vs forEach performance in javascript and credibility of jsperf results

Diamond Hands picture Diamond Hands · Mar 17, 2012 · Viewed 13.5k times · Source

I don't trust results from jsperf measuring performance of for loop vs forEach. At least for chrome and firefox on my machine results are completely different than the ones being advertised in jsperf.
http://jsperf.com/foreach-vs-loop (mine)
http://jsben.ch/#/BQhED (more popular)
On my laptop running Ubuntu 11.10 I have the following results in Firefox:

for: total=1641 ms, avg=164.1 ms  
forEach: total=339 ms, avg=33.9 ms  

uname -a:  
Linux 3.0.0-16-generic #29-Ubuntu SMP Tue Feb 14 12:48:51 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

Unfortunately, Chrome doesn't return the result of console.timeEnd() but the running times are same and just faster in Chrome. I'm observing that forEach almost 10x faster than for loop in Chrome, and 3x faster in Firefox.
In Chrome I'm getting approximately these running times:

for: avg=80 ms
forEach: avg=6 ms

Here's the code I ran in Firefox and Chrome console.

var arr = [];
for(var i = 0; i < 100000; i++) arr[i]=i;

var numberOfRuns = 10;

function time(name, f){
    console.time(name);
    f();
    return console.timeEnd(name);
}

function runTest(name, f){
    var totalTime = 0;
    for(var r = 0; r < numberOfRuns; r++)
        totalTime += time(name,f);
    return totalTime;
}

var forTime = runTest('for', function(){
    for(var j = 0; j < arr.length; j++)
        arr[j];    
});
var forEachTime = runTest('forEach', function(){
    arr.forEach(function(v){v;});
});

console.log('for', {total:forTime, avg:forTime / numberOfRuns});
console.log('forEach', {total:forEachTime, avg:forEachTime / numberOfRuns});

Running the tests for one million items has the same performance difference. Could you please advise if I'm missing something and I should trust jsperf results instead of the real ones I'm observing? Of course I do trust the real results that I can see right here right now in my browser.

EDIT: The test scenario isn't objective as discovered during discussion with @Blender. Looks like js optimizer optimezes forEach loop with no action in it and thus obscures running time if there were some real code.

Answer

Blender picture Blender · Mar 17, 2012

I modified your code to be more fair. Can you take a look at it?

var arr = [];
for (var i = 0; i < 100000; i++) arr[i] = i;

var numberOfRuns = 100;

function runTest(name, f) {
    var totalTime = 0;
    console.time(name);

    for (var r = 0; r < numberOfRuns; r++) {
        f();
    }

    return console.timeEnd(name);
}

function testFunction(v) {
    v;
}

var forTime = runTest('for', function() {
    for (var j = 0; j < arr.length; j++) {
        testFunction(arr[j]);
    }
});

var forEachTime = runTest('forEach', function() {
    arr.forEach(testFunction);
});

Your test wasn't 100% raw number crunching, so the benchmark was being optimized unfairly by some browsers.