62

Given this code:

var arr = [];

for (var i = 0; i < 10000; ++i)
    arr.push(1);

Forwards

for (var i = 0; i < arr.length; ++i) {}

Backwards

for (var i = arr.length - 1; i >= 0; --i) {}

Hard-coded Forward

for (var i = 0; i < 10000; ++i) {}

Why is backwards so much faster?

Here is the test: http://jsperf.com/array-iteration-direction

David Callanan
  • 5,601
  • 7
  • 63
  • 105
samccone
  • 10,746
  • 7
  • 43
  • 50
  • 7
    Note that your backwards case should start at `arr.length-1` and have `i >= 0` rather than `i > 0`. – nnnnnn Jan 01 '12 at 02:30
  • 2
    The title is misleading. It is no longer the case that directly using arr.length *is* slower in advanced browsers. – user2864740 Oct 04 '14 at 07:05
  • possible duplicate of [JavaScript loop performance - Why is to decrement the iterator toward 0 faster than incrementing](http://stackoverflow.com/q/3520688/1048572) – Bergi Jun 27 '15 at 16:50

7 Answers7

90

Because your forwards-condition has to receive the length property of your array each time, whilst the other condition only has to check for "greater then zero", a very fast task.

When your array length doesn't change during the loop, and you really look at ns-perfomance, you can use

for (var i=0, l=arr.length; i<l; i++)

BTW: Instead of for (var i = arr.length; i > 0; --i) you might use for (var i = arr.length; i-- > 0; ) which really runs through your array from n-1 to 0, not from n to 1.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Yep, this is how I do it for optimized functions. Just note that if the array is mutable (and it usually isn't), this may skip a resource or two. For example, this won't work if you're looping through elements that the loop itself is adding or deleting. – Jeffrey Sweeney Dec 31 '11 at 18:11
  • 4
    you can even do `for(... ; i-- ;)` – ajax333221 Jun 06 '12 at 22:00
  • 2
    so basically it means that if I don't change the array and I keep array.length in a variable, then backwards speed = forward speed? Example: var length = array.length; for(var i = 0 ; i – Breno Inojosa Apr 04 '14 at 23:07
  • afaik, `i0`, but yes they're rather equal (faster than uncached length): http://jsperf.com/array-iteration-direction/3 - yet some engines do recognize and optimize some of these cases – Bergi Apr 05 '14 at 08:45
  • The condition of a loop is evaluated on each iteration. Think of it like `var i = 0; var a = i < 10; while(a) { i++; a = i < 10; }`. – bryc Aug 27 '14 at 18:33
  • @bryc: So what? Of course it needs to be. Did I say something else? – Bergi Aug 28 '14 at 08:09
  • Isnt this loop `for (var i = arr.length; i-- > 0; )` going to miss 0 index as 1-- !>0? maybe this one needs to be `for (var i = arr.length; i-- >= 0; )` – Max Yari Apr 02 '15 at 21:02
  • @MaxYari: No. The `i` in the loop body (after being decremented) is always one smaller than the `i` seen in the condition (before being decremented). What you would need is `for (var i=arr.length-1; i>=0; --i)` – Bergi Apr 02 '15 at 21:03
  • tested, yes 0 is here, i honestly did not understand yet why, isnt it in the first place decrements `i`, then compares to 0, then decides to run iteration or not. Like this, it seems that 1 must be decremented to 0, therefore not starting iteration as it's not > 0 – Max Yari Apr 02 '15 at 21:10
  • @MaxYari: Read on [prefix/postfix decrement operators](http://stackoverflow.com/q/6867876/1048572) then – Bergi Apr 02 '15 at 21:14
  • Oh snap!! you opened my eyes, what a shame, thank you) – Max Yari Apr 02 '15 at 21:16
  • 3
    Fast forward to 2018. it looks like the modern versions of browsers don't make loop slower even when accessing arr.length in each iteration (your forwards case) – Gaurang Patel Jan 19 '18 at 19:26
9

Because in the first form you are accessing the property length of the array arr once for every iteration, whereas in the second you only do it once.

rabusmar
  • 4,084
  • 1
  • 25
  • 29
  • @samccone - what result are you seeing (which test in which browser)? I'm seeing that caching the length is always at least as fast and usually faster. – jfriend00 Dec 31 '11 at 18:04
  • alright you are correct... it looks like it is really a mixed bag of results tho but caching does help it... thanks XD – samccone Dec 31 '11 at 21:32
7

If you want to have them at same pace, you can do that for forward iteration;

for(var i=0, c=arr.length; i<c; i++){
}

So, your script won't need to take length of array on everystep.

tcak
  • 2,142
  • 1
  • 16
  • 23
  • Ahhh... interesting your method now takes the crown for fastest iteration http://jsperf.com/array-iteration-direction – samccone Dec 31 '11 at 17:50
5

I am not entirely sure about this, but here is my guess:

For the following code:

for (var i = 0; i < arr.length; ++i) {;
}

During runtime, there is an arr.length calculation after each loop pass. This may be a trivial operation when it stands alone, but may have an impact when it comes to multiple/huge arrays. Can you try the following:

 var numItems = arr.length;
    for(var i=0; i< numItems; ++i)
    {
    }

In the above code, we compute the array length just once, and operate with that computed number, rather than performing the length computation over and over again.

Again, just putting my thoughts out here. Interesting observation indeed!

Gopal Nair
  • 830
  • 4
  • 7
3

i > 0 is faster than i < arr.length and is occurring on each iteration of the loop.

You can mitigate the difference with this:

for (var i = 0, len = arr.length; i < len; ++i) {;
}

This is still not as fast as the backwards item, but faster than your forward option.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • look at the hard coded test http://jsperf.com/array-iteration-direction ... using your logic it should be faster no? – samccone Dec 31 '11 at 17:45
2

And these are equally good:

var arr= [], L= 10000;
while(L>-1) arr[L]= L--;

OR

var arr= [], i= 0;
while(i<10001) arr[i]=i++;
Noctis
  • 11,507
  • 3
  • 43
  • 82
kennebec
  • 102,654
  • 32
  • 106
  • 127
2

do it like below, it will perform in same way. because arr.length takes time in each iteration in forward.

int len = arr.length;

forward

for (var i = 0; i < len; ++i) {
}

backward

for (var i = len; i > 0; --i) {
}
dku.rajkumar
  • 18,414
  • 7
  • 41
  • 58