I've been watching Google Tech Talks' Speed Up Your Javascript and in talking about loops, the speaker mentions to stay away from function-based iterations such as jQuery.each() (among others, at about 24:05 in the video). He briefly explains why to avoid them which makes sense, but admittedly I don't quite understand what an alternative would be. Say, in the case I want to iterate through a column of table cells and use the value to manipulate the adjacent cell's value (just a quick example). Can anyone explain and give an example of an alternative to function-based iteration?
views:
396answers:
5Just a simple for loop should be quicker if you need to loop.
var l = collection.length;
for (var i = 0; i<l; i++) {
//do stuff
}
But, just because it's quicker doesn't mean it's always important that it is so.
This runs at the client, not the server, so you don't need to worry about scaling with the number of users, and if it's quick with a .each(), then leave it. But, if that's slow, a for loop could speed it up.
It seems to me that it would be case that function-based iteration would be slightly slower because of the 1) the overhead of function itself, 2) the overhead of the callback function being created and executed N times, and 3) the extra depth in the scope chain. However, I thought I'd do a quick benchmark just for kicks. Turns out, at least in my simple test-case, that function-based iteration was faster. Here's the code and the findings
Test benchmark code
// Function based iteration method
var forEach = function(_a, callback) {
for ( var _i=0; _i<_a.length; _i++ ) {
callback(_a[_i], _i);
}
}
// Generate a big ass array with numbers 0..N
var a = [], LENGTH = 1024 * 10;
for ( var i=0; i<LENGTH; i++ ) { a.push(i); }
console.log("Array length: %d", LENGTH);
// Test 1: function-based iteration
console.info("function-base iteration");
var end1 = 0, start1 = new Date().getTime();
var sum1 = 0;
forEach(a, function(value, index) { sum1 += value; });
end1 = new Date().getTime();
console.log("Time: %sms; Sum: %d", end1 - start1, sum1);
// Test 2: normal for-loop iteration
console.info("Normal for-loop");
var end2 = 0, start2 = new Date().getTime();
var sum2 = 0;
for (var j=0; j<a.length; j++) { sum2 += a[j]; }
end2 = new Date().getTime();
console.log("Time: %sms; Sum: %d", end2 - start2, sum2);
Each test just sums the array which is simplistic, but something that can be realistically seen in some sort of real life scenario.
Results for FF 3.5
Array length: 10240
function-base iteration
Time: 9ms; Sum: 52423680
Normal for-loop
Time: 22ms; Sum: 52423680
Turns out that a basic for
iteration was faster in this test case. I haven't watched the video yet, but I'll give it a look and see if he's differing somewhere that would make function-based iterations slower.
Edit: This is by no means the end-all, be-all and is only the results of one engine and one test-case. I fully expected the results to be the other way around (function-based iteration being slower), but it is interesting to see how certain browsers have made optimizations (which may or may not be specifically aimed at this style of JavaScript) so that the opposite is true.
If the order of looping doesn't matter, the following should be fastest as you only need a single local variable; also, decrementing the counter and bounds-checking are done with a single statement:
var i = foo.length;
if(i) do { // check for i != 0
// do stuff with `foo[i]`
} while(--i);
What I normally use is the following:
for(var i = foo.length; i--; ) {
// do stuff with `foo[i]`
}
It's potentially slower than the previous version (post- vs pre-decrement, for
vs while
), but more readable.
The fastest possible way to iterate is to cut down on stuff you do within the loop. Bring stuff out of the iteration and minimise lookups/increments within the loop, e.g.
var i = arr.length;
while (i--) {
console.log("Item no "+i+" is "+arr[i]);
}
NB! By testing on the latest Safari (with WebKit nightly), Chrome and Firefox, you'll find that it really doesn't matter which kind of the loop you're choosing if it's not for each
or for in
(or even worse, any derived functions built upon them).
Also, what turns out, is that the following for
loop is very slightly even faster than the above option:
var l = arr.length;
for (var i=l; i--;) {
console.log("Item no "+i+" is "+arr[i]);
}