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").
The fastest loop is a for loop, both with and without caching length delivering really similar performance.
The traditional for loop is the fastest, so you should always use that right? Not so fast — performance is not the only thing that matters. It is rare that you will ever need to loop over 1 million items in a frontend JS app.
In theory the while loop is quicker because the for loop looks up the length attribute of foo every time though the loop, but in real-world use it's going to make an immeasurably small difference.
Deductions. This foreach loop is faster because the local variable that stores the value of the element in the array is faster to access than an element in the array. The forloop is faster than the foreach loop if the array must only be accessed once per iteration.
I'm not sure about Javascript, and under modern compilers it probably doesn't matter, but in the "olden days" this code:
for (i = 0; i < n; i++){
.. body..
}
would generate
move register, 0
L1:
compare register, n
jump-if-greater-or-equal L2
-- body ..
increment register
jump L1
L2:
while the backward-counting code
for (i = n; --i>=0;){
.. body ..
}
would generate
move register, n
L1:
decrement-and-jump-if-negative register, L2
.. body ..
jump L1
L2:
so inside the loop it's only doing two extra instructions instead of four.
I believe the reason is because you're comparing the loop end point against 0, which is faster then comparing again < length
(or another JS variable).
It is because the ordinal operators <, <=, >, >=
are polymorphic, so these operators require type checks on both left and right sides of the operator to determine what comparison behaviour should be used.
There's some very good benchmarks available here:
What's the Fastest Way to Code a Loop in JavaScript
It is easy to say that an iteration can have less instructions. Let’s just compare these two:
for (var i=0; i<length; i++) {
}
for (var i=length; i--;) {
}
When you count each variable access and each operator as one instruction, the former for
loop uses 5 instructions (read i
, read length
, evaluate i<length
, test (i<length) == true
, increment i
) while the latter uses just 3 instructions (read i
, test i == true
, decrement i
). That is a ratio of 5:3.
What about using a reverse while loop then:
var values = [1,2,3,4,5];
var i = values.length;
/* i is 1st evaluated and then decremented, when i is 1 the code inside the loop
is then processed for the last time with i = 0. */
while(i--)
{
//1st time in here i is (length - 1) so it's ok!
process(values[i]);
}
IMO this one at least is a more readble code than for(i=length; i--;)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With