views:

396

answers:

5

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?

+3  A: 

Just 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.

Chad
The fasting way of iterating in javascript isdo{ // code to be executed}while (--currentIndex)Of course you can only use this when order doesn't matter (or reversed order is desirable)
klaaspieter
I normally use `for(var i = foo.length; i--; ) {}`; a `do..while` loop with pre-decrement might be faster, but in practice the difference rarely matters
Christoph
@klaaspieter, you could simply reverse the order by using ++currentIndex and, before the block, do currentIndex *= -1;
Dykam
@Dykam, that's just hard to read, it works, but it's overly obfuscated
Chad
+3  A: 

Ye olde for-loop

Jani Hartikainen
A: 

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.

Justin Johnson
It's because you used "j<a.length;" as your condition. Each time the condition is checked, as you have it written, it needs to check the length of the array. If your loop won't change the array, store the length in a variable and use that variable in your condition.
Chad
I am fully aware of that optimization; however, it shouldn't have any affect on this test as you'll notice that both `for` loops are identical in structure and lack of optimization. But, as the first test showed unexpected results, I'll run it again with that optimization to see if it changes anything, but I'm not convinced at this point that it will. Both iterations should decrease by a similar amount.
Justin Johnson
@chad As I suspected, this optimization didn't change the test results significantly. In fact, I would consider this a false optimization with this data structure. For live data structures (like dom containers), this optimization is useful.
Justin Johnson
I just ran your script several times in IE 7 and the results were different than yours. Function based looping taking a consistent 31 or 32ms, and standard for loops taking 15 or 16ms.
Chad
Sorry, I should have specified that this was in FF3.5 (edited).
Justin Johnson
You must have something else running and slowing it down, b/c in FF 3.5 with the 10240 items you were using I get 0-1ms in either method. If I up the number to 10240000 I get, function-base iteration: Time: 145ms; Sum: 52428794880000 - Normal for-loop: Time: 125ms; Sum: 52428794880000
Chad
If something was running to slow it down, it would make sense that both tests would be slow or that the results would be inconsistent, neither of which is the case. I ran my test with your paramaters and got: `Array length: 10240000 function-base iteration Time: 11504ms; Sum: 52428794880000 Normal for-loop Time: 26665ms; Sum: 52428794880000` I just booted my machine and the only thing I have open is TextEdit and FF3.5
Justin Johnson
@Justin, well something weird is happening. As there's is no mathematical way that I can think of for a function that uses the same for loop internally to be faster than that for loop standing alone. The same speed, maybe, but unlikely. But faster just doesn't make any sense. Especially by the margins you're seeing, a few % of the speed would make sense, with fluctuations in the other things the processor is doing. Try flipping the order of tests around and see if there's a difference?
Chad
@Chad I agree that it is unexpected. I too thought to flip the order, but no change was seen. I've tried running the results on a different machine (a MacBook as opposed to the iMac used earlier) and the results are similar.
Justin Johnson
@Chad FF 3.0 has the same results (Favoring function based iteration)
Justin Johnson
A: 

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.

Christoph
A: 

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]);
}
Ain