Performance optimizations and for loops

[2013-07-10] dev, javascript, jslang
(Ad, please don’t block)
This blog post looks at two common performance optimizations for for loops. Are they really faster than the canonical version?

Performance optimizations?

The canonical for loop looks like this:
    for(var i=0; i < arr.length; i++) {
        console.log(arr[i]);
    }
Given that the above code seemingly accesses the property length of arr for every iteration, you often read the recommendation to avoid that check. There are two main ways of doing so. First, you can cache that property’s value in a local variable:
    for(var i=0, len=arr.length; i < len; i++) {
        console.log(arr[i]);
    }
Second, you can count down instead of counting up:
    for(var i=arr.length; i >= 0; i--) {
        console.log(arr[i]);
    }
I find these seemingly performance-optimized versions harder to read. Are they really worth it, are they really faster? If you measure their performances, you find out that:
  • The canonical way performs roughly as well as the optimized versions, with a few notable exceptions.
  • Caching the length while counting up is never slower than not caching it.
  • Counting down behaves unpredicatably: It can be faster or slower than counting up.
  • As expected, Array.prototype.forEach is much slower than a for loop.

Array.prototype.forEach

Written with forEach, the running example looks like this:
    arr.forEach(function (x) {
        console.log(x);
    });
I like forEach: it is syntactically convenient and creates a fresh copy of the iteration variables per iteration. That prevents a common JavaScript bug: Some functions should receive the current value of an iteration variable when they are created, instead of tracking the value of that variable [1].

ECMAScript 6

ECMAScript 6 will have a new loop: for-of [2].
    for (let x of arr) {
        console.log(x);
    }
It is as syntactically convenient as forEach and it also creates a fresh copy of x per iteration. But those are only two of its benefits [2].

Recommendations

Don’t optimize performance prematurely and measure carefully to figure out what to optimize and to figure out whether you are really optimizing on all JavaScript engines.

Similarly, don’t be needlessly clever with your code. Usually, it’s more important that your code is easy to understand and correct than that it is fast. JavaScript engines automatically optimize more and more of the established patterns, which means that you have to have a good reason for veering off the established path.

I find forEach loops easier to understand than for loops. I suspect that JavaScript engines can and will optimize their execution even further in the future.

To me, it is important to keep code as local as possible. Hence, I like to declare variables inside a loop that are only used there. This is a bit more controversial, but it does not normally incur a performance penality [3].

References

  1. JavaScript quirk 7: inadvertent sharing of variables via closures
  2. Iterators and generators in ECMAScript 6
  3. Variable declarations: three rules you can break