Math Increments: Pre vs Post, Inc vs Dec

Incrementing through a sequence is arguably the most basic programming method. It is done again and again in programming, in every language. For example:

 for (int i = 0; i < 10; i++) { /* do something with i */ };

Where the "do something" might be indexing the elements of an array (in which case the "10" should be the arrays length) or any number of other things. And this "for loop" code is compiled into machine language millions of times a day, either by pre-compilers for the C or C++ languages or "JIT" (Just in Time) compilers like javascript, running on this web page. Nearly every microcontroller has an "increment register" or "increment memory location" instruction which runs very quickly. It doesn't seem like there is any room for improvement nor any point to discuss it further.

Why it matters

But think for a movement about what the i++ really does. It returns a value and is "post increment" meaning that it first returns the value of i, then increments it. Of course, it can't return before it increments so what really happens is this:

// i++ becomes:
int j = i; //we must save a copy of the original value of i
i = i + 1; //now we can increment it.
return j; //then we can return the original value. And dispose of j(?)

Look at the effort there: We had to allocate a new variable, copy i into it, increment i, then return that new variable, which will eventually need to be deallocated; probably immediately because the return value is rarely used.

What if we used pre-increment? ++i has the same effect in our for loop example above, because we don't really care about the returned value. But the code is so much simpler:

// ++i becomes:
i = i + 1; //just increment
return i; //and return

And since we never use the return value, a good compiler will see that the return value isn't used and optimize that instruction out.

Why it doesn't matter (probably)

In fact, a good compiler (JIT or otherwise), when given even the post-increment code, will probably realize that we don't need to make a copy and return it if the returned value isn't used. Because this poor programming practice is so commonly used, compiler authors are very likely to grab it as low hanging fruit and do that optimization. So all the problems described above are probably already optimized out for you

And modern computers are so fast that executing the 10 or so extra machine language instructions required isn't ever going to be noticed.

Why it actually does matter

1. Many compilers are stupid and will not optimize it out. And because this code is almost always in a loop, it's small effect is multiplied. And then again multiplied by the number of times this code is used. The extra energy consumption is significant in the long run / big picture.

2. Old, wise, and sharp programmers understand the advantage and will recognize your better coding. Young, inexperienced, or dull programmers will be curious why you wrote it that way, and you will have an opportunity to demonstrate your mastery of coding. Or to look like a pedantic ass, your choice.

3. Because, darn it, it's better code!

Increment vs Decrement

There is another level to this, specific to for loops. In our example above, every time around the loop, we have to compare the value of i with the number 10. This requires:

// is i < 10?
int j = 10 - i; //store the value of i - 10
if (j > 0) {} //check if that is positive.

Why do we have to subtract 10? Because processors don't have a way to test against every possible value of i; they have circuits which test against zero, positive, negative, etc... So again, we are allocating another variable (because we can't alter i), doing a subtraction, then testing the result and tossing the temporary value. How can we avoid this? Easy:

for (int i = 10; i > 0; --i) { /* do something 10 times */ };

That isn't exactly the same, since i will go from 10 down to 1 vs 0 up to 9. But we still go through the loop 10 times. If we need to index an array, this might be better:

for (int i = 9; i >= 0; --i) { /* do something with i in reverse order */ };

Both i > 0 and i >= 0 compile down to a single instruction on almost all processors. One checks the =0? flag, the other checks the overflow? flag. Of course, that still isnt the same, since it's going from 9 down to 0, vs 0 up to 9. But in most cases, the order you index the array doesn't matter. For more detail on that, you can look at how comparisons are done on the popular Microchip PIC microcontroller. Note that after an increment or decrement machine language instruction, those flags are also set, just as they are after a subtract.

What you should write:

for (int i = 10; i > 1; --i) { /* do something 10 times */ };

for (int i = 9; i >= 0; --i) { /* do something with i in reverse order */ };

for (int i = 0; i < 10; ++i) { /* do something with i in order from 0 to 9 */ };

And in any case, if your language has a for...each or for...in, use that. e.g. javascript