JavaScript loop performance - Why is to decrement the iterator toward 0 faster than incrementing

Viewed 31780

In his book Even Faster Web Sites Steve Sounders writes that a simple way to improve the performance of a loop is to decrement the iterator toward 0 rather than incrementing toward the total length (actually the chapter was written by Nicholas C. Zakas). This change can result in savings of up to 50% off the original execution time, depending on the complexity of each iteration. For example:

var values = [1,2,3,4,5];
var length = values.length;

for (var i=length; i--;) {
   process(values[i]);
}

This is nearly identical for the for loop, the do-while loop, and the while loop.

I'm wondering, what's the reason for this? Why is to decrement the iterator so much faster? (I'm interested in the technical background of this and not in benchmarks proving this claim.)


EDIT: At first sight the loop syntax used here looks wrong. There is no length-1 or i>=0, so let's clarify (I was confused too).

Here is the general for loop syntax:

for ([initial-expression]; [condition]; [final-expression])
   statement
  • initial-expression - var i=length

    This variable declaration is evaluated first.

  • condition - i--

    This expression is evaluated before each loop iteration. It will decrement the variable before the first pass through the loop. If this expression evaluates to false the loop ends. In JavaScript is 0 == false so if i finally equals 0 it is interpreted as false and the loop ends.

  • final-expression

    This expression is evaluated at the end of each loop iteration (before the next evaluation of condition). It's not needed here and is empty. All three expressions are optional in a for loop.

The for loop syntax is not part of the question, but because it's a little bit uncommon I think it's interesting to clarify it. And maybe one reason it's faster is, because it uses less expressions (the 0 == false "trick").

12 Answers

It's not faster (at least in modern browsers):

// Double loops to check the initialization performance too
const repeats = 1e3;
const length = 1e5;

console.time('Forward');
for (let j = 0; j < repeats; j++) {
  for (let i = 0; i < length; i++) {}
}
console.timeEnd('Forward'); // 58ms

console.time('Backward');
for (let j = repeats; j--;) {
  for (let i = length; i--;) {}
}
console.timeEnd('Backward'); // 64ms

The difference is even bigger in case of an array iteration:

const repeats = 1e3;
const array = [...Array(1e5)];

console.time('Forward');
for (let j = 0; j < repeats; j++) {
  for (let i = 0; i < array.length; i++) {}
}
console.timeEnd('Forward'); // 34ms

console.time('Backward');
for (let j = 0; j < repeats; j++) {
  for (let i = array.length; i--;) {}
}
console.timeEnd('Backward'); // 64ms

Related