ChatGPT解决这个技术问题 Extra ChatGPT

Are loops really faster in reverse?

I've heard this quite a few times. Are JavaScript loops really faster when counting backward? If so, why? I've seen a few test suite examples showing that reversed loops are quicker, but I can't find any explanation as to why!

I'm assuming it's because the loop no longer has to evaluate a property each time it checks to see if it's finished and it just checks against the final numeric value.

I.e.

for (var i = count - 1; i >= 0; i--)
{
  // count is only evaluated once and then the comparison is always on 0.
}
hehe. that will take indefinetely. try i--
Backwards for loop is faster because the upper-bound (hehe, lower-bound) loop control variable does not have to be defined or fetched from an object; it is a constant zero.
There is no real difference. Native loop constructs are always going to be very fast. Don't worry about their performance.
@Afshin: For questions like this, please show us the articles you're referring to.
There is difference mainly important for very low-end and battery powered devices. The differences is that with i-- you compare to 0 for the end of the loop, while with i++ you compare with number > 0. I believe the performance difference was something like 20 nanoseconds (something like cmp ax,0 vs. cmp ax,bx) - which is nothing but if you loop thousands of times per second, why not have 20 nanoseconds gain for each :)

J
Joel Christophel

It's not that i-- is faster than i++. Actually, they're both equally fast.

What takes time in ascending loops is evaluating, for each i, the size of your array. In this loop:

for(var i = array.length; i--;)

You evaluate .length only once, when you declare i, whereas for this loop

for(var i = 1; i <= array.length; i++)

you evaluate .length each time you increment i, when you check if i <= array.length.

In most cases you shouldn't even worry about this kind of optimization.


is it worth to introduce a variable for array.length and use it in the for loop's head?
@ragatskynet: No, unless you are setting up a benchmark and want to make a point.
@ragatskynet It depends: will it be faster to evaluate .length a certain number of times or to declare and define a new variable? In most cases, this is premature (and wrong) optimization, unless your length is very expensive to evaluate.
@Dr.Dredel: It's not the comparison - it's the evaluation. 0 is faster to evaluate than array.length. Well, supposedly.
What is worth mentioning is that we are talking about interpreted languages such as Ruby, Python. Compiled languages e.g. Java have optimizations on compiler level which which will "smooth" these differences to the point that it does not matter if .length is in declaration of for loop or not.
B
Baldrickk

This guy compared a lot of loops in javascript, in a lot of browsers. He also has a test suite so you can run them yourself.

In all cases (unless I missed one in my read) the fastest loop was:

var i = arr.length; //or 10
while(i--)
{
  //...
}

Nice :) But there is no backward "for"-loop tested... But the for loops mentioned by peirix or searlea should be pretty much the same as the "while" loop with "i--" as its condition. And that was the fastest loop tested.
Interesting, but I wonder if pre-decrement would be even faster. Since it won't have to store the intermediate value of i.
If I remember correctly, my hardware-course prof said, that test for 0 or not 0 is fastest possible "calculation". In while(i--) the test is always a test for 0. Maybe that's why this is fastest?
@tvanfosson if you pre-decrement --i then you have to use var current = arr[i-1]; inside the loop or it will be off by one...
I've heard that i-- can be faster than --i because in the second case the processor needs to decrement and then test against the new value (there's a data dependency between the instructions), while in the first case the processor can test the existing value and decrement the value some time later. I'm not sure if this applies to JavaScript or only very low-level C code.
P
Peter Mortensen

I try to give a broad picture with this answer.

The following thoughts in brackets was my belief until I have just recently tested the issue:

[[In terms of low level languages like C/C++, the code is compiled so that the processor has a special conditional jump command when a variable is zero (or non-zero).
Also, if you care about this much optimization, you could go ++i instead of i++, because ++i is a single processor command whereas i++ means j=i+1, i=j.]]

Really fast loops can be done by unrolling them:

for(i=800000;i>0;--i)
    do_it(i);

It can be way slower than

for(i=800000;i>0;i-=8)
{
    do_it(i); do_it(i-1); do_it(i-2); ... do_it(i-7);
}

but the reasons for this can be quite complicated (just to mention, there are the issues of processor command preprocessing and cache handling in the game).

In terms of high level languages, like JavaScript as you asked, you can optimize things if you rely on libraries, built-in functions for looping. Let them decide how it is best done.

Consequently, in JavaScript, I would suggest using something like

array.forEach(function(i) {
    do_it(i);
});

It is also less error-prone and browsers have a chance to optimize your code.

[REMARK: not only the browsers, but you too have a space to optimize easily, just redefine the forEach function (browser dependently) so that it uses the latest best trickery! :) @A.M.K. says in special cases it is worth rather using array.pop or array.shift. If you do that, put it behind the curtain. The utmost overkill is to add options to forEach to select the looping algorithm.]

Moreover, also for low level languages, the best practice is to use some smart library function for complex, looped operations if it is possible.

Those libraries can also put things (multi-threaded) behind your back and also specialized programmers keep them up-to-date.

I did a bit more scrutiny and it turns out that in C/C++, even for 5e9 = (50,000x100,000) operations, there is no difference between going up and down if the testing is done against a constant like @alestanis says. (JsPerf results are sometimes inconsistent but by and large say the same: you can't make a big difference.)
So --i happens to be rather a "posh" thing. It only makes you look like a better programmer. :)

On the other hand, for-unrolling in this 5e9 situation, it has brought me down from 12 sec to 2.5 sec when I went by 10s, and to 2.1 sec when I went by 20s. It was without optimization, and optimization has brought things down to unmeasureable little time. :) (Unrolling can be done in my way above or using i++, but that does not bring things ahead in JavaScript. )

All in all: keep i--/i++ and ++i/i++ differences to the job interviews, stick to array.forEach or other complex library functions when available. ;)


The key word is "can be". Your unrolled loop might also be slower than the original one. When optimizing, always measure so you know exactly what impact your changes had.
@jalf true indeed, +1. Different unlooping lenghts(>=1) are different in efficiency. That is why it is more convenient to leave this job to libraries if possible, not to mention that browsers run on different architectures so it might be better if they decide how to do array.each(...). I don't think they would try and experiment with unlooping plain for-loops.
@BarnabasSzabolcs The question is for JavaScript specifically, not C or other languages. In JS there is a difference, please see my answer below. Although not applicable to the question, +1 good answer!
And there you go - +1 to get you a Great Answer Badge. Really great answer. :)
APL/A++ is not a language either. People use these to express that they are not interested in language specifics, but something that those languages share.
s
sainiuc

i-- is as fast as i++

This code below is as fast as yours, but uses an extra variable:

var up = Things.length;
for (var i = 0; i < up; i++) {
    Things[i]
};

The recommendation is to NOT evaluate the size of the array each time. For big arrays one can see the performance degradation.


You are plainly wrong here. Now loop control needs extra internal variable (i and up) and depending on JavaScript engine this may limit potential to optimize the code. JITs will translate simple loops to direct machine opcodes, but if loops have too many variables then JIT won't be able to optimize as good. Generally, it's limited by architecture or cpu registers that JIT uses. Initializing once and going down simply cleanest solution and that's why it's recommended all over the place.
The additional variable will be eliminated by the optimizer of a common interpreter/compiler. The point is the 'compare to 0' - see my answer for further explanation.
Better to put 'up' inside the loop: for (var i=0, up=Things.length; i<up; i++) {}
Why internal variable could create issues? This is mistaken because the loop control won't have an "extra variable" because up exactly as Things.length is not passed by reference (as an object or array would be) but directly by value. In other words, it is inserted in the loop control exactly as the number 10 or 100000. You just renamed it OUTSIDE of the loop, so no difference whatsoever. For this reason the answer in itself is entirely valid. When seeing i < up the loop control sees i < 10 not i < (a reference to number 10). Actually up stores inside it a primitive, don't forget that.
d
dreamcrash

Since, you are interested in the subject, take a look at Greg Reimer's weblog post about a JavaScript loop benchmark, What's the Fastest Way to Code a Loop in JavaScript?:

I built a loop benchmarking test suite for different ways of coding loops in JavaScript. There are a few of these out there already, but I didn't find any that acknowledged the difference between native arrays and HTML collections.

You can also do a performance test on a loop by opening https://blogs.oracle.com/greimer/resource/loop-test.html (does not work if JavaScript is blocked in the browser by, for example, NoScript).

EDIT:

A more recent benchmark created by Milan Adamovsky can be performed in run-time here for different browsers.

For a Testing in Firefox 17.0 on Mac OS X 10.6 I got the following loop:

https://i.stack.imgur.com/yn6kQ.png


@dreamcash On Chrome 88.x, all reverse loops are always faster than all forward loops. jsben.ch/eng8b, measurethat.net/Benchmarks/ShowResult/162677, jsbench.me/5ykkzsysa9 . Sometimes reverse-for, sometimes reverse-optimized-for, sometimes reverse-while.
@johnywhy okey I miss-understood okey so it still makes a bit of a difference. Will you post an answer with those results?
P
Peter Mortensen

It's not the -- or ++, it is the compare operation. With -- you can use a compare with 0, while with ++ you need to compare it with the length. On the processor, compare with zero is normally available, while compare with a finite integer requires a subtraction.

a++ < length

is actually compiled as

a++
test (a-length)

So it takes longer on the processor when compiled.


B
BBog

I've seen the same recommendation in Sublime Text 2.

Like it was already said, the main improvement is not evaluating the array's length at each iteration in the for loop. This a well-known optimization technique and particularly efficient in JavaScript when the array is part of the HTML document (doing a for for the all the li elements).

For example,

for (var i = 0; i < document.getElementsByTagName('li').length; i++)

is much slower than

for (var i = 0, len = document.getElementsByTagName('li').length; i < len; i++)

From where I'm standing, the main improvement in the form in your question is the fact that it doesn't declare an extra variable (len in my example)

But if you ask me, the whole point is not about the i++ vs i-- optimization, but about not having to evaluate the length of the array at each iteration (you can see a benchmark test on jsperf).


I have to take exception to the word calculating here. See my comment on Pavel's answer. The ECMA spec states that the array length is not calculated when you refer to it.
Would 'evaluating' be a better choice? Interesting fact btw, I did not know that
The additional variable will be eliminated by the optimizer of a common interpreter/compiler. The same applies to the array.length evaluation. The point is the 'compare to 0' - see my answer for further explanation.
@H.-DirkSchmitt your common-sense answer aside, for long in the history of JavaScript the compiler did not optimize away the performance cost of the lookup chain. AFAIK V8 was the first to try.
@H.-DirkSchmitt just like kojiro said, this is as well-known and well-established trick. Even if it is no longer relevant in modern browsers, that still doesn't make it obsolete. Besides, doing it with either introducing a new variable for length or with the trick in OP's question, it's still the best way, imo. It's just a smart thing to do and good practice, I do not think it has anything to do with the fact that the compiler was optimized to take care of something often done in a bad way in JS
C
Community

Short answer

For normal code, especially in a high level language like JavaScript, there is no performance difference in i++ and i--.

The performance criteria is the use in the for loop and the compare statement.

This applies to all high level languages and is mostly independent from the use of JavaScript. The explanation is the resulting assembler code at the bottom line.

Detailed explanation

A performance difference may occur in a loop. The background is that on the assembler code level you can see that a compare with 0 is just one statement which doesn't need an additional register.

This compare is issued on every pass of the loop and may result in a measurable performance improvement.

for(var i = array.length; i--; )

will be evaluated to a pseudo code like this:

 i=array.length
 :LOOP_START
 decrement i
 if [ i = 0 ] goto :LOOP_END
 ... BODY_CODE
 :LOOP_END

Note that 0 is a literal, or in other words, a constant value.

for(var i = 0 ; i < array.length; i++ )

will be evaluated to a pseudo code like this (normal interpreter optimisation supposed):

 end=array.length
 i=0
 :LOOP_START
 if [ i < end ] goto :LOOP_END
 increment i
 ... BODY_CODE
 :LOOP_END

Note that end is a variable which needs a CPU register. This may invoke an additional register swapping in the code and needs a more expensive compare statement in the if statement.

Just my 5 cents

For a high level language, readability, which facilitates maintainability, is more important as a minor performance improvement.

Normally the classic iteration from array start to end is better.

The quicker iteration from array end to start results in the possibly unwanted reversed sequence.

Post scriptum

As asked in a comment: The difference of --i and i-- is in the evaluation of i before or after the decrementing.

The best explanation is to try it out ;-) Here is a Bash example.

 % i=10; echo "$((--i)) --> $i"
 9 --> 9
 % i=10; echo "$((i--)) --> $i"
 10 --> 9

1+ Good explanation. Just a question a little out of scope, could you please explain the difference between --i and i-- also?
P
Pavel P

I don't think that it makes sense to say that i-- is faster that i++ in JavaScript.

First of all, it totally depends on JavaScript engine implementation.

Secondly, provided that simplest constructs JIT'ed and translated to native instructions, then i++ vs i-- will totally depend on the CPU that executes it. That is, on ARMs (mobile phones) it's faster to go down to 0 since decrement and compare to zero are executed in a single instruction.

Probably, you thought that one was waster than the other because suggested way is

for(var i = array.length; i--; )

but suggested way is not because one faster then the other, but simply because if you write

for(var i = 0; i < array.length; i++)

then on every iteration array.length had to be evaluated (smarter JavaScript engine perhaps could figure out that loop won't change length of the array). Even though it looks like a simple statement, it's actually some function that gets called under the hood by the JavaScript engine.

The other reason, why i-- could be considered "faster" is because JavaScript engine needs to allocate only one internal variable to control the loop (variable to the var i). If you compared to array.length or to some other variable then there had to be more than one internal variable to control the loop, and the number of internal variables are limited asset of a JavaScript engine. The less variables are used in a loop the more chance JIT has for optimization. That's why i-- could be considered faster...


It's probably worth careful phrasing about how array.length is evaluated. The length is not calculated when you refer to it. (It's just a property that gets set whenever an array index is created or changed). When there is additional cost, it is because the JS engine has not optimized the lookup chain for that name.
Well, not sure what Ecma spec says, but knowing about some internals of different JavaScript engines it's not simple getLength(){return m_length; } because there is some housekeeping involved. But if you try to think backwards: that would be quite ingenious to write array implementation where length would need to be actually calculated :)
the ECMA spec requires that the length property be already calculated. The length must be updated immediately whenever a property-that-is-an-array-index is added or changed.
What I'm trying to say is that it's pretty hard to violate the spec if you try think about it.
x86 is like ARM in that respect. dec/jnz vs. inc eax / cmp eax, edx / jne.
A
A.M.K

Since none of the other answers seem to answer your specific question (more than half of them show C examples and discuss lower-level languages, your question is for JavaScript) I decided to write my own.

So, here you go:

Simple answer: i-- is generally faster because it doesn't have to run a comparison to 0 each time it runs, test results on various methods are below:

Test results: As "proven" by this jsPerf, arr.pop() is actually the fastest loop by far. But, focusing on --i, i--, i++ and ++i as you asked in your question, here are jsPerf (they are from multiple jsPerf's, please see sources below) results summarized:

--i and i-- are the same in Firefox while i-- is faster in Chrome.

In Chrome a basic for loop (for (var i = 0; i < arr.length; i++)) is faster than i-- and --i while in Firefox it's slower.

In Both Chrome and Firefox a cached arr.length is significantly faster with Chrome ahead by about 170,000 ops/sec.

Without a significant difference, ++i is faster than i++ in most browsers, AFAIK, it's never the other way around in any browser.

Shorter summary: arr.pop() is the fastest loop by far; for the specifically mentioned loops, i-- is the fastest loop.

Sources: http://jsperf.com/fastest-array-loops-in-javascript/15, http://jsperf.com/ipp-vs-ppi-2

I hope this answers your question.


It seems that your pop test seems only quite so fast because it is reducing the array size down to 0 for the majority of the loop - as far as I can tell. However to make sure things are fair I've designed this jsperf to create the array in the same way with each test - which seems to show .shift() as being the winner for my few browsers - not what I would have expected though :) jsperf.com/compare-different-types-of-looping
Upvoted for being the only one to mention ++i :D
d
dreamcrash

It depends on placement of your array in memory and the hit ratio of memory pages while you are accessing that array.

In some cases accessing array members in column order is faster than row order because of the increase in hit ratio.


If only the OP had asked why traversing the same matrix in different orders can take different amounts of time..
Considering that in Operating Systems that their memory management is based on Paging, when a process needs a data that is not in cached pages , occurs a page fault in OS and it must bring target page to CPU cache and replace with another page, therefore , causes overhead in processing that takes more time than when target page is in CPU cache. Suppose that we defined a large array that each row in it is bigger than page OS size and we access it in row order, in this case page fault ratio increases and result is slower than column order access to that array.
Page faults aren't the same thing as cache misses. You only page fault if your memory got paged out to disk. Accessing multidimensional arrays in sequential order of how they're stored in memory is faster because of cache locality (using all bytes of a cache line when its fetched), not because of page faults. (unless your working set is too big for the computer you're using.)
P
Peter Mortensen

The last time I bothered about it was when writing 6502 assembly (8-bit, yeah!). The big gain is that most arithmetic operations (especially decrements) updated a set of flags, one of them was Z, the 'reached zero' indicator.

So, at the end of the loop you just did two instructions: DEC (decrement) and JNZ (jump if not zero), no comparison needed!


In case of JavaScript obviously it doesn't apply (since it runs on CPUs that don't have such op-codes). Most likely the real reason behind the i-- vs i++ is that with the former you don't introduce extra control variables in the loop's scope. See my answer below...
right, it really doesn't apply; but it's a very common C style, and it does looks cleaner to those of us that got used to that. :-)
x86 and ARM are both like 6502 in this respect. dec/jnz instead of inc/cmp/jne for the x86 case. You won't see an empty loop run any faster (both will saturate branch throughput), but counting down slightly reduces loop overhead. Current Intel HW prefetchers aren't bothered by memory access patterns that go in descending order, either. Older CPUs I think could track like 4 backwards streams and 6 or 10 forward streams, IIRC.
p
peirix

The way you're doing it now isn't faster (apart from it being an indefinite loop, I guess you meant to do i--.

If you want to make it faster do:

for (i = 10; i--;) {
    //super fast loop
}

of course you wouldn't notice it on such a small loop. The reason it's faster is because you're decrementing i while checking that it's "true" (it evaluates to "false" when it reaches 0)


Are you missing a semi-colon? (i = 10; i--;)
Yes yes, I fixed the mistake. And if we're going to be picky I'll point out you forgot your semi colon after your i--! Heh.
Why would that be faster? Shorter source doesn't mean it will be faster. Have you measured it?
Yes, I have measured it, and I'm not saying shorter source makes it faster, but fewer operations makes it faster.
Here's a benchmark demonstrating the difference - jsbench.me/1cknepoalw/1
s
searlea

It can be explained by JavaScript (and all languages) eventually being turned into opcodes to run on the CPU. CPUs always have a single instruction for comparing against zero, which is damn fast.

As an aside, if you can guarantee count is always >= 0, you could simplify to:

for (var i = count; i--;)
{
  // whatever
}

Shorter source code doesn't necessarily mean it will be faster. Have you measured it?
Ooops I missed this one. Nail on the head there chap.
I'd like to see the assembly sources where comparing against 0 is different. It's been a few years, but at one time I did a ton of assembly coding and I can't for the life of me think of a way to do a compare/test against 0 in a way that can't be equally as fast for any other integer. Yet, what you say does ring true. Frustrated that I can't figure out why!
@Brian Knoblauch: If you use an instruction such as "dec eax" (x86 code) then that instruction automatically sets the Z (zero) flag which you can immediately test without having to use another compare instruction in between.
When interpretting Javascript, I doubt that opcodes are going to be the bottleneck, though. More likely to be that less tokens means the interpreter can process the source code faster.
P
Peter Mortensen

for(var i = array.length; i--; ) is not much faster. But when you replace array.length with super_puper_function(), that may be significantly faster (since it's called in every iteration). That's the difference.

If you are going to change it in 2014, you don't need to think about optimization. If you are going to change it with "Search & Replace", you don't need to think about optimization. If you have no time, you don't need to think about optimization. But now, you've got time to think about it.

P.S.: i-- is not faster than i++.


C
Community

To cut it short: There is absolutely no difference in doing this in JavaScript.

First of all, you can test it yourself:

jsperf - is an excellent platform for all sorts of performance testing in JavaScript.

http://jsperf.com/inc-vs-dec-2

Not only can you test and run any script in any JavaScript library, but you also have access to the whole bunch of previously written scripts, as well as the abilty to see differences between execution time in different browsers on different platforms.

So as far as you can see, there is no difference between performance in any environment.

If you want to improve performance of your script, things you can try to do:

Have a var a = array.length; statement so that you will not be calculating its value each time in the loop Do loop unrolling http://en.wikipedia.org/wiki/Loop_unwinding

But you have to understand that the improvement you can gain will be so insignificant, that mostly you should not even care about it.

My own opinion why such a misconception (Dec vs Inc) appeared

A long, long time ago there was a common machine instruction, DSZ (Decrement and Skip on Zero). People who programmed in assembly language used this instruction to implement loops in order to save a register. Now this ancient facts are obsolete, and I am pretty sure you will not get any performance improvement in any language using this pseudo improvement.

I think the only way such knowledge can propagate in our time is when you read another's person code. See such a construction and ask why was it implemented and here the answer: "it improves performance because it compares to zero". You became bewildered of higher knowledge of your colleague and think to use it to be much smarter :-)


Interesting, but for your tests running under firefox 16.0.2 on win7, the decrement loop was 29% slower... EDIT: Disregard that. Repeated tests proved inconclusive, there is a surprising amount of noise in my test results for subsequent runs. Not quite sure why.
Yeah I tried to account for that by closing everything else down and just running the tests. Still got wonky results. Very strange.
I think you missed the real point why going to zero is considered to be better in JavaScript. It's mostly because this way only one variable controls loop execution, e.g. this way optimizer/JITer has more room for improvement. Using array.length doesn't necessarily incur performance penalty, simply because JS virtual machine is smart enough to figure out if the array isn't modified by the loop's body. See my answer below.
nitpicking: this ancient fact (assembly language optimizations) is not obsolete, just arcane. as in you don't need to know unless you really do. :-)
S
Spyryto

I made a comparison on jsbench.

As alestani pointed out, one thing that takes time in ascending loops, is to evaluate, for each iteration, the size of your array. In this loop:

for ( var i = 1; i <= array.length; i++ )

you evaluate .length each time you increment i. In this one:

for ( var i = 1, l = array.length; i <= l; i++ )

you evaluate .length only once, when you declare i. In this one:

for ( var i = array.length; i--; )

the comparison is implicit, it happens just before decrementing i, and the code is very readable. However, what can make a terrific difference, is what you put inside the loop.

Loop with call to function (defined elsewhere):

for (i = values.length; i-- ;) {
  add( values[i] );
}

Loop with inlined code:

var sum = 0;
for ( i = values.length; i-- ;) {
  sum += values[i];
}

If you can inline your code, instead of calling a function, without sacrificing legibility, you can have a loop an order of magnitude faster!

Note: as browser are becoming good at inlining simple functions, it really depends on how complex your code is. So, profile before optimizing, because

The bottleneck may be elsewhere (ajax, reflow, ...) You may choose a better algorithm You may choose a better data structure

But remember:

Code is written for people to read, and only incidentally for machines to execute.


+1 for this answer and for the benchmark. I've add forEach tests and reworked the benchmark to a standalone file runnable in browser as well as in Node. jsfiddle.net/hta69may/2. For Node "Reverse loop, implicit comparison, inlined code" is the fastest. But testing in FF 50 shown curious results: not only the timings were almost 10 times less (!) but both "forEach" tests were as fast as "reverse loop". Maybe Node guys should use Mozilla's JS engine instead of V8? :)
P
Peter Mortensen

This is not dependent on the -- or ++ sign, but it depends on conditions you apply in the loop.

For example: Your loop is faster if the variable has a static value than if your loop check conditions every time, like the length of an array or other conditions.

But don't worry about this optimization, because this time its effect is measured in nanoseconds.


multiple nanoseconds loops can become seconds ... it's never a bad idea to optimize when you have time for it
S
Salman A

++ vs. -- does not matter because JavaScript is an interpreted language, not a compiled language. Each instruction translates to more than one machine language and you should not care about the gory details.

People who are talking about using -- (or ++) to make efficient use of assembly instructions are wrong. These instruction apply to integer arithmetic and there are no integers in JavaScript, just numbers.

You should write readable code.


A
Ant

It used to be said that --i was faster (in C++) because there is only one result, the decremented value. i-- needs to store the decremented value back to i and also retain the original value as the result (j = i--;). In most compilers this used up two registers rather than one which could cause another variable to have to be written to memory rather than retained as a register variable.

I agree with those others that have said it makes no difference these days.


benchmarks are all over the place: jsben.ch: --i is faster, jsben.ch/RpG0K. jsbench.me: i-- is faster, jsbench.me/i2kkzuk4kl/1. measurethat.net: --i is faster, measurethat.net/Benchmarks/ShowResult/162675.
K
Kunwar Siddharth Singh

Sometimes making some very minor changes to the way that we write our code can make a big difference to how quickly our code actually runs. One area where a minor code change can make a big difference to execution times is where we have a for loop that is processing an array. Where the array is of elements on the web page (such as radio buttons) the change has the biggest effect but it is still worth applying this change even where the array is internal to the Javascript code.

The conventional way of coding a for loop to process an array lis like this:

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

The problem with this is that evaluating the length of the array using myArray.length takes time and the way that we have coded the loop means that this evaluation has to be performed every time around the loop. If the array contains 1000 elements then the length of the array will be evaluated 1001 times. If we were looking at radio buttons and had myForm.myButtons.length then it will take even longer to evaluate since the appropriate group of buttons within the specified form must first be located before the length can be evaluated each time around the loop.

Obviously we don't expect the length of the array to change while we are processing it so all of these recalculations of the length are just adding unnecessarily to the processing time. (Of course if you have code inside the loop that adds or removes array entries then the array size can change between iterations and so we can't change the code that tests for it)

What we can do to correct this for a loop where the size is fixed is to evaluate the length once at the start of the loop and save it in a variable. We can then test the variable to decide when to terminate the loop. This is much faster than evaluating the array length each time particularly when the array contains more than just a few entries or is part of the web page.

The code to do this is:

for (var i = 0, var j = myArray.length; i < j; i++) {...

So now we only evaluate the size of the array once and test our loop counter against the variable that holds that value each time around the loop. This extra variable can be accessed much faster than evaluating the size of the array and so our code will run much faster than before. We just have one extra variable in our script.

Often it doesn't matter what order we process the array in as long as all of the entries in the array get processed. Where this is the case we can make our code slightly faster by doing away with the extra variable that we just added and processing the array in reverse order.

The final code that processes our array in the most efficient way possible is:

for (var i = myArray.length-1; i > -1; i--) {...

This code still only evaluates the size of the array once at the start but instead of comparing the loop counter with a variable we compare it with a constant. Since a constant is even more effective to access than a variable and since we have one fewer assignment statement than before our third version of the code is now slightly more efficient than the second version and vastly more efficient than the first.


A
Artelius

In many cases, this has essentially nothing to do with the fact that processors can compare to zero faster than other comparisons.

This is because only a few Javascript engines (the ones in the JIT list) actually generate machine language code.

Most Javascript engines build an internal representation of the source code which they then interpret (to get an idea of what this is like, have a look near the bottom of this page on Firefox's SpiderMonkey). Generally if a piece of code does practically the same thing but leads to a simpler internal representation, it will run faster.

Bear in mind that with simple tasks like adding/subtracting one from a variable, or comparing a variable to something, the overhead of the interpreter moving from one internal "instruction" to the next is quite high, so the less "instructions" that are used internally by the JS engine, the better.


t
the swine

Well, I don't know about JavaScript, it should really be just a matter of re-evaluation array length and maybe something to do with the associative arrays (if you only decrement, it is unlikely new entries would need to be allocated - if the array is dense, that is. someone may optimize for that).

In low-level assembly, there is a looping instruction, called DJNZ (decrement and jump if non-zero). So the decrement and jump is all in one instruction, making it possibly ever-so-slightly faster than INC and JL / JB (increment, jump if less than / jump if below). Also, comparing against zero is simpler than comparing against another number. But all that is really marginal and also depends on target architecture (could make difference e.g. on Arm in a smartphone).

I wouldn't expect this low-level differences to have so great impact on interpreted languages, I just haven't seen DJNZ among the responses so I thought I would share an interesting thought.


For the record, DJNZ is an instruction in the 8051 (z80) ISA. x86 has dec/jnz instead of inc/cmp/jne, and apparently arm has something similar. I'm pretty sure this isn't the cause of the Javascript difference; that's from having more to eval in the loop condition.
R
Ravi_Parmar

In very simple words

"i-- and i++. Actually, they're both takes the same time".

but in this case when you have incremental operation.. processor evaluate the .length every time variable is incremented by 1 and in case of decrement.. particularly in this case, it will evaluate .length only once till we get 0.


D
Dmitry

First, i++ and i-- take exactly the same time on any programming language, including JavaScript.

The following code take much different time.

Fast:

for (var i = 0, len = Things.length - 1; i <= len; i++) { Things[i] };

Slow:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

Therefore the following code take different time too.

Fast:

for (var i = Things.length - 1; i >= 0; i--) { Things[i] };

Slow:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

P.S. Slow is slow only for a few languages (JavaScript engines) because of compiler's optimization. The best way is to use '<' instead '<=' (or '=') and '--i' instead 'i--'.


P
Peter Mortensen

Not a lot of time is consumed by i-- or i++. If you go deep inside the CPU architecture the ++ is more speedy than the --, since the -- operation will do the 2's complement, but it happend inside the hardware so this will make it speedy and no major difference between the ++ and -- also these operations are considered of the least time consumed in the CPU.

The for loop runs like this:

Initialize the variable once at the start.

Check the constraint in the second operand of the loop, <, >, <=, etc.

Then apply the loop.

Increment the loop and loop again throw these processes again.

So,

for (var i = Things.length - 1; i >= 0; i--) {
    Things[i]
}; 

will calculate the array length only once at the start and this is not a lot of time, but

for(var i = array.length; i--; ) 

will calculate the length at each loop, so it will consume a lot of time.


var i = Things.length - 1; i >= 0; i-- will calculate length 1 time too.
I'm not sure what "-- operation will do the 2's complement" means, but I assume it means that it will negate something. No, it doesn't negate anything on any architecture. Subtracting 1 is just as simple as adding 1, you just make a circuit that borrows rather than carries.
G
Greg Hewgill

The best approach to answering this sort of question is to actually try it. Set up a loop that counts a million iterations or whatever, and do it both ways. Time both loops, and compare the results.

The answer will probably depend on which browser you are using. Some will have different results than others.


That still wouldn't answer his question on why it's faster. He'd just get a benchmark, without any knowledge of why it is that way...
That's true. However, without knowing the exact implementation of each Javascript engine in each browser, it's nearly impossible to answer the "why" part. A lot of the answers here throw around anecdotal recommendations like "use predecrement instead of postdecrement" (a C++ optimisation trick) and "compare against zero" which might be true in native code compiled languages but Javascript is pretty far from the bare metal of the CPU.
-1 I completely disagree that the best approach is to try it. A couple examples don't replace collective knowledge, and that's the whole point of forums like this one.
R
Robert

Love it, lots of marks up but no answer :D

Simply put a comparison against zero is always the fastest comparison

So (a==0) is actually quicker at returning True than (a==5)

It's small and insignificant and with 100 million rows in a collection it's measurable.

i.e on a loop up you might be saying where i <= array.length and be incrementing i

on a down loop you might be saying where i >= 0 and be decrementing i instead.

The comparison is quicker. Not the 'direction' of the loop.


There is no answer to the question as stated because Javascript engines are all different and the answer depends on exactly which browser you're measuring it on.
No it's fundamentally accepted comparisons against zero are the quickest. Although your statements are correct as well the golden rule of comparing against zero is absolute.
That's true only if the compiler chooses to make that optimisation, which is certainly not guaranteed. The compiler might generate exactly the same code for (a==0) and (a==5), except for the value of the constant. A CPU won't compare against 0 any more quickly than against any other value, if both sides of the comparison are variables (from the point of view of the CPU). Generally only native code compilers have the opportunity to optimise at this level.
m
mrbinky3000

HELP OTHERS AVOID A HEADACHE --- VOTE THIS UP!!!

The most popular answer on this page does not work for Firefox 14 and does not pass the jsLinter. "while" loops need a comparison operator, not an assignment. It does work on chrome, safari, and even ie. But dies in firefox.

THIS IS BROKEN!

var i = arr.length; //or 10
while(i--)
{
  //...
}

THIS WILL WORK! (works on firefox, passes the jsLinter)

var i = arr.length; //or 10
while(i>-1)
{
  //...
  i = i - 1;
}

I just tried it on Firefox 14 and it worked fine. I have seen examples from production libraries that use while (i--) and which work across many browsers. Could there have been something weird about your test? Were you using a beta version of Firefox 14?
R
Rorchackh

This is just a guess, but maybe it's because it's easier for the processor to compare something with 0 ( i >= 0 ) instead of with another value ( i < Things.length).


+1 Don't others have voted down. Although the repeated evaluation of .length is much more a problem than the actual inc/decrement, the check for the loop end might be important. My C compiler gives some warnings on loops like: "remark #1544-D: (ULP 13.1) Detected loop counting up. Recommend loops count down as detecting zeros is easier"