Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can anyone explain how this loop manages to benchmark so high?

http://jsperf.com/loops/67

If you look, the following loop manages some insane benchmarks:

var i=0;
var v;
for (i, v; v = arr[i++];) {
   v;
}

It score ~700 million ops/sec in FF, ~20 mil in Chrome, and ~50mil in IE10. The next fastest loop manages ~100k in FF, ~6k in IE10 and barely ~2k in Chrome.

Why is it so fast? I can see the obvious differences between the other loops and how one is faster than another, but I can't come up with anything that would explain the absolutely mind blowing performance difference with this loop, 700 million to 100k is an insane gap.


Edit after answers:

Based off @Michael Gary's answer, I went back and edited the setup to include an actual real array and the results fell back to reality: http://jsperf.com/loops/70

like image 599
Enzo Avatar asked Apr 07 '13 03:04

Enzo


1 Answers

The reason is simple. The array arr is created with this code:

var arr = new Array(10000);

So it has a length of 10000, but all of the elements are undefined. This loop doesn't work off the array length, but terminates when it encounters a "falsy" value - the assumption being that the loop will stop when v receives an undefined value as a result of trying to read past the end of the array.

But in this particular array, all ten thousand elements have the value undefined. So the loop stops when it tests the very first element of the array. In other words, it doesn't loop at all! No wonder it's fast.

But what about a more real-world case? How does this kind of loop fare with a lengthy JSON array of objects:

[
    { "id": 507674, "name": "Kolink" },
    { "id": 997356, "name": "DarkLord7854" },
    { "id": 1202830, "name": "Michael Geary" },
    /* and thousands more */
]

Here you don't have the problem of the loop terminating immediately, since the array elements are all "truthy".

With modern JavaScript engines this turns out to be a fairly poor way to write a loop, as I recently found out to my extreme embarrassment.

I was one of the authors of the jQuery Cookbook: I wrote most of Chapter 5, "Faster, Simpler, More Fun". Well, the "faster" part didn't turn out so well. I was recommending a loop very much like yours for iterating over a large array of objects like the one above:

for( var item, i = -1;  item = array[++i]; ) {
    // do stuff with item
}

Turns out that in modern browsers, this is quite a bit slower than a conventional loop like this:

for( var i = 0, n = array.length;  i < n;  i++ ) {
    var item = array[i];
    // do stuff with item
}

Part of this is due to the fact that trying to read past the end of the array throws some JavaScript engines back into a deoptimized way of representing the array, as one of the V8 authors explained to me at Google I/O last year. Part of it may be due to the browsers optimizing the more common kinds of loops and not optimizing this less common approach.

Either way, the more conventional loop turns out to be faster in modern browsers:

http://jsperf.com/mikes-loops/2

But that's a different case from your loop. In yours, the insane performance increase is directly due to the fact that it doesn't run the loop at all. :-)

like image 137
Michael Geary Avatar answered Sep 27 '22 22:09

Michael Geary