Eric Hynds

I love to build things for the web.

← all posts

JavaScript's .length Property is a Stored Value

Wednesday, December 09, 2009

I was reading this article from SitePoint earlier about how inefficient the JavaScript is behind Google's new Closure library, and saw some interesting discussion about caching an array's length property before entering a 'for' loop. This is what I'm talking about:

// uncached length property
for(var x=0; x < myArray.length; x++){}

// cached length property
for(var x=0, len=myArray.length; x < len; x++){}

I saw this tip and didn't think twice about it; I've always been told that caching the length property is indeed faster. I then saw a couple comments saying otherwise:

Anyway, the loop performance is not as big deal as you present. Of course, it's nicer and more professional to preassign the array length instead of testing it again in every step. But the performance is not such problem. The Javascript basic operations are very fast, making the difference quite unnoticeable. Those two loops performance will differ only in milliseconds for a million-iterations loop in today's average computer. This means the same loop traversing an array containing a million items will take about 15 ms if the array.length is tested every time, and about 10 ms if the length is preassigned. So, we are talking about saving nanoseconds per a thousand-iterations loop. This makes no sense from the performance point of view.

And:

These are nearly Identical in performance. Even if the loop does nothing, so that the time spent in the loop is all spent incrementing i and checking its value, it is STILL hard to detect the performance difference. And if you do anything of substance in this loop, the time spent checking against someArray.length is dwarfed by the loop body. This is a stupid complaint.

I have always blindly assumed that the performance hit was due to the interpreter calculating the .length value each time it is referenced, but after talking to some folk on freenode, I discovered that .length is actually a stored value. Since JavaScript doesn't need to re-calculate it on every iteration of the loop, where would the performance hit come from? Anyway, I thought it would be fun to run some benchmarks of my own to see the difference. Below is little script I put together, but please take it as a grain of salt because JavaScript time is not accurate.

var array = [], start, diff;
var results = { nocache:[], cached:[] };

// build array to loop over
for(var x = 0; x < 1000000; x++) {
    array[x] = x;
}

// run tests 25 times
for(var x = 0; x < 25; x++) {
    // length not cached
    start = new Date().getTime();
    for(var y = 0; y < array.length; y++) {}
    results.nocache[x] = new Date().getTime() - start;

    // length cached
    start = new Date().getTime();
    for(var y = 0, len = array.length; y < len; y++) {}
    results.cached[x] = new Date().getTime() - start;
}

// output results
document.write( '
  No cache time: ' + avg(results.nocache) + ' Values: ' + results.nocache.join(',') + '' );
  document.write( '<p>Cached time: ' + avg(results.cached) + ' Values: ' + results.cached.join(',') + '
');

// http://javascript.about.com/library/blaravg.htm
function avg(arr){
    var av = 0, cnt = 0, e, len = arr.length;

    for(var i = 0; i < len; i++){
        e = +arr[i];
        if(!e && arr[i] !== 0 && arr[i] !== '0') e--;
        if (arr[i] == e){ av += e; cnt++; }
    }

    return av/cnt;
}

The code first builds an array with 1,000,000 items, records the execution time of looping over the array 25 times using both methods, and then figures the average. [Here are the results:

The results are what I expected except in the Windows versions of Firefox 3.5.2/3.5.5. In all other browsers I tested it is faster to cache the .length value, but in FF3.5 for Windows it is actually faster not to cache. Internet Explorer receives the most benefit with a ~30% increase in performance when caching, with Opera (Linx) and Chrome (Linux) in fourth and fifth place respectively. The amount of time is indeed negligible, but every millisecond counts, right?

If nothing else it is interesting to see how long it takes different browsers to loop through a large array. Firefox and Safari FTW!

comments powered by Disqus