Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

array.forEach running faster than native iteration? How?

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

like image 687
Sean Anderson Avatar asked Apr 02 '12 18:04

Sean Anderson


2 Answers

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.

like image 114
jbabey Avatar answered Sep 25 '22 01:09

jbabey


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.

like image 30
Erik Reppen Avatar answered Sep 23 '22 01:09

Erik Reppen