9

http://jsperf.com/testing-foreach-vs-for-loop

It was my understanding that Test Case 2 should run more slowly than Test Case 1 -- I wanted to see how much more slowly. Imagine my surprise when I see it runs more quickly!

What's going on here? Behind the scenes optimizaiton? Or is .forEach cleaner AND faster?

Testing in Chrome 18.0.1025.142 32-bit on Windows Server 2008 R2 / 7 64-bit

holographic-principle
  • 19,688
  • 10
  • 46
  • 62
Sean Anderson
  • 27,963
  • 30
  • 126
  • 237
  • 1
    `forEach` probably has some native optimizations, or something. – gen_Eric Apr 02 '12 at 18:20
  • 3
    What's more, you're not even getting the value from the array in Test Case 1, you're logging i instead of `array[i]` – huysentruitw Apr 02 '12 at 18:22
  • 1) you shouldn't create the array on every test, you can define vars that will be accessible on all tests 2) you are checking `.length` every loop – ajax333221 Apr 02 '12 at 18:41
  • If you run version 7 http://jsperf.com/testing-foreach-vs-for-loop/7 the results are interesting and the issues mentioned in these comments have been corrected. – kiswa Apr 02 '12 at 18:46

5 Answers5

11

There are many iteration optimizations that your for loop is missing such as:

  • cache the array length
  • iterate backwards
  • use ++counter instead of counter++

These are the ones that I have heard of and used, I am sure there are more. If memory serves me correct, the backwards iterating while loop is the fastest of all looping structures (in most browsers).

See this jsperf for some examples.

Edit: links for postfix vs prefix perf test and iterating backwards. I was not able to find my reference for using +=1 instead of ++, so I have removed it from the list.

Community
  • 1
  • 1
jbabey
  • 45,965
  • 12
  • 71
  • 94
  • I have heard that for C that ++i is faster then i++, don't know if that also applies to Javascript – huysentruitw Apr 02 '12 at 18:24
  • iterate backwards and using `+=1` instead of `++` actually changes nothing in JS. – kirilloid Apr 02 '12 at 18:26
  • 1
    @kirilloid += and ++ are two completely different operators which could run at different speeds in different parser implementations, and decrementing vs incrementing is a well known micro-optimization. i will edit to add references. – jbabey Apr 02 '12 at 18:32
  • Unfortunately `js` is still far from the speed level of e.g. `C`. In both `C` and `js` iterating backwards may cut off one (or maybe 2) CPU operation, but one op in `JS` loop is too small. JS parsers may as well optimize. Needless to say, dead code elimination optimizer may just throw off some code in your example. And completely mess the results. – kirilloid Apr 02 '12 at 18:40
  • Note: the post linked to the backwards looping is probably obsolete since browsers have changed a lot. I have tested many stuff that claimed to have a difference in speed but found none on updated browsers. (I am not saying this is the case, but just be aware that browsers evolve) – ajax333221 Apr 02 '12 at 18:48
  • @ajax333221 No way. But `i++` is equal in speed to `i--`, the same for `++i` and `--i`; `i+=1` and `i-=1`. Even if there's some speed difference between `i++` and `++i`, one may use corresponding decremental operation. – kirilloid Apr 02 '12 at 18:49
  • the link you pointed to the postfix vs prefix clearly says that `i++` is faster than `++i`. But you are suggesting the opposite? – ajax333221 Apr 02 '12 at 18:56
  • I just tested pure speed of prefix/postfix increment (100 unrolled ops in large loop) and `++i` is 15-20% faster than `i++` in FF10 / WinXP 32 bit. – kirilloid Apr 02 '12 at 19:03
  • @ajax333221 [this SO question](http://stackoverflow.com/questions/1546981/post-increment-vs-pre-increment-javascript-optimization) states that prefix (++i) is faster than postfix (i++). Whether or not it is faster in a specific browser is another question... – jbabey Apr 02 '12 at 19:06
  • @ErikReppen avoiding off-by-one errors is a different topic than loop performance and optimizations. whether or not the person wants to trade readability and maintainability for the performance is the real question. – jbabey Apr 02 '12 at 19:30
  • I didn't explain that well. The perf win of i-- is that you simply evaluate via while(i--) in a while loop rather than (i someValue). I added details on what i-- vs --i to explain how that works. I also gave a bum example of a for loop (bum because I forgot evaluation and decrement weren't happening on the spot in a for loop), which is why I zapped the comment. – Erik Reppen Apr 02 '12 at 19:39
3

UPDATE:

A lot of the old tricks in these answers are great for interpreted JS in older browsers.

In any modern JS implementation including all modern browsers, Node, and the latest mobile webviews, inline functions can actually be cached by the JIT (JS compiler), making forEach a much faster option for array iteration, typically. It used to be the opposite where just making calls to a function repeatedly required a build-up/teardown process that could severely diminish performance of a non-trivial loop.

For best performance I'd avoid referencing anything that wasn't passed as an argument or defined inside the function itself if you don't have to. I'm not 100% sure that matters but I could see why it might.

Getter values that involve any kind of lookup process like array lengths or DOM node properties are probably also still best cached to a variable.

But beyond that I'd try to just let the basic principle of work avoidance guide your perf efforts. Pre-calculating things that don't need to be recalculated in a loop, or caching a query selector result to var rather than rummaging in the DOM repeatedly are good examples of this. Trying too hard to take advantage of JIT behavior is probably going to get pretty arcane and not likely to hold up over time or across all JITs.

OLD ANSWER:

Okay, forget wall of text. Bullet points:

var i = someArray.length; //length is cached
someArray.reverse(); //include this only if iterating in 0-(length-1) order is important

while(i--){
//run a test statement on someArray[i];
}
  • length is cached and immediately made into the index

  • The benefit of iterating backwards in JS AFAIK is avoiding a logical operator with two operands. In this case we're just evaluating a number. It's true or it's zero and false.

  • I also find it elegant.

Erik Reppen
  • 4,605
  • 1
  • 22
  • 26
1

Reading length from array at each iteration may be slow, but forEach is comomonly slower, cause function call isn't cheap operation in js.

PS: forEach is 14% slower on FF10.

kirilloid
  • 14,011
  • 6
  • 38
  • 52
  • I think it still matters but I believe the JITs have made function calls considerably zippier. – Erik Reppen Apr 02 '12 at 19:43
  • Me too =) Especially after that, I sped up CPU-heavy JS code ~5 times with just inlining functions. – kirilloid Apr 02 '12 at 20:07
  • No matter what I try, forEach is always faster in Chrome. – trusktr Dec 02 '15 at 01:06
  • Didn't know this 4 year ago but it's because modern JITs actually cache inline functions in the right circumstances. In old interpreters the buildup/teardown process of a function call made them undesirable in loops. Now they can actually be performance optimizing. It's probably best to avoid referencing anything that isn't defined in the function or passed to it as an argument. – Erik Reppen Apr 05 '16 at 17:53
0

They're approximately the same for me in Opera. Something to note is that your conditional in the for() is array.length. If you cache the length of the array in a variable, and then loop, you should see better performance.

Jordan
  • 31,971
  • 6
  • 56
  • 67
0

Maybe the for() is slower because the loop applies 'array.length' to each iteration, to get the array's length.

Try:

var nri = array.length;
for( var i = 0; i < nri; i++ ){
   // ...
}
CoursesWeb
  • 4,179
  • 3
  • 21
  • 27