Fog Creek Software
Discussion Board




Preincrement vs. postincrement

I was looking through the Guru of the Week archives ( http://www.gotw.ca/gotw/index.htm ) and came across this item in the issue on temporary objects ( http://www.gotw.ca/gotw/002.htm ):

"Preincrement is more efficient than postincrement, because for postincrement the object must increment itself and then return a temporary containing its old value. Note that this is true even for builtins like int!"

For objects this makes sense, but I haven't found it to be true for int's.  An instructor told me years ago that all my loop indexes should be preincremented because it's more efficient.  So, I tested this by compiling a small C program with two for loops, something like:

for ( ; ; i++ ) { ... }

-- and --

for ( ; ; ++i) { ... }

I found no difference between the two in the resulting assembly file.  My assumption is that compilers have just optimized it, so is this really an issue anymore?

Yet another anon
Friday, August 06, 2004

Turn off optimisation and find out (yes, there are some places where compiler optimised output is not allowed).


Friday, August 06, 2004

I had this worry myself :) For native types like int the compiler indeed optimizes it away (I checked), but with more complex objects it might choke (I didn't check).

I usually post-increment native types (can't break the habit) and pre-increment only iterators etc.

Alex
Friday, August 06, 2004

Also from that article:

[Rule] Follow the single-entry/single-exit rule. Never write multiple return statements in the same function.

I speak from very limited experience, but it doesn't seem tremendously difficult to implement return-value optimization. From what I've peeked at, the caller itself preallocates N bytes on the stack and THEN makes the call.

The called function forms the object directly in the space reserved, and returns. The callee now has a valid object right on top of the stack.

Alex
Friday, August 06, 2004

It depends on the processor and what is being done with he result whether or not preincrement or postincrement is faster, or even the same. For example, postincrement is faster than preincrement on the motorola 68000 series because postincrementing can be done in a single operation.

Tony Chang
Friday, August 06, 2004

Yes, the compiler will optimise it out for int, now try it with an iterator over a std::map.

More to the point, however, you should use preincrement because it expresses what you actually mean. Preincrement means 'incrememnt this', postincrement means 'increment this and then give me it's original value' - use whichever fits your intended meaning better.

Mr Jack
Friday, August 06, 2004

Tony - I think the point is to avoid (possibly unnecesary) object creation caused by the "give me its previous value" indicated in Mr Jack's post. I doubt that the 68000 can do that in one operation :-)


Friday, August 06, 2004

Uh? That sounds wrong. Certainly the "C" specification says that the post increment happens at the end of the statement so simple code that trys to convert two ascii digits into decimal like this:

x=(*(c++) -'0')*10+(*(c++) -'0');

can be legitimately compiled (and has been by at least one compiler I know) into:
x=(*c -'0')*10+(*c -'0'); c+=2;

The increments happen at the end of the statement. No extra copy required.

I don't know what the C++ spec says about this but I'd guess it was the same.

Peter Ibbotson
Friday, August 06, 2004

Are you sure about that Peter? Have you actually checked the specs?

In C++ the bit of code you just described has undefined behaviour, and I'd be very, very surprised in the C++ version was more loosely defined than the C one (unless they introduced the change in C99, that is).

I refer you to http://www.kuzbass.ru/docs/isocpp/expr.html - note 5.

Mr Jack
Friday, August 06, 2004

I can't quite see it in there the only relevant bit I can see is:

-4- Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.

My brief test with MS VC6.0 with this code (I've made x a global to stop the compiler optimising the second increment away).
char *x;
int ParseIt()
{
    return (*(x++)-'0')*10+(*(x++)-'O');
}
Compiling with
CL /Ox /Fa /c test.cpp

Produces this assembler output:

?ParseIt@@YAHXZ PROC NEAR        
    mov    ecx, DWORD PTR ?x@@3PADA        movsx    eax, BYTE PTR [ecx]
    sub    eax, 48                add    ecx, 2
    mov    DWORD PTR ?x@@3PADA, ecx        lea    edx, DWORD PTR [eax+eax*4]
    lea    eax, DWORD PTR [eax+edx*2]
    ret    0
?ParseIt@@YAHXZ ENDP    

Better yet the current VS.NET 2005 compiler produces this output:
?ParseIt@@YAHXZ PROC                    ; ParseIt
    mov    ecx, DWORD PTR ?x@@3PADA        movsx    eax, BYTE PTR [ecx]
    imul    eax, 11                add    ecx, 2
    sub    eax, 545                mov    DWORD PTR ?x@@3PADA, ecx        ret    0
?ParseIt@@YAHXZ ENDP            

An interesting question at this point is what does gcc do? I don't have a copy handy any more, so I can't say.

Peter Ibbotson
Friday, August 06, 2004

Bugger, somehow the formatting got screwed up!
Anyway try it and see.

Peter Ibbotson
Friday, August 06, 2004

"The operations implied by the postincrement and postdecrement     operators ++ and -- are performed at some time after the operand's former values are yielded and before the end of the     expression, but not necessarily immediately after, or before other parts of the expression are evaluated."

... says the comp.lang.C FAQ

Using a variable the state of which changes during the same expression is not illegal, but the results are either implementation defined or altogether undefined.

(If it's implementation defined it means that in theory your compiler vendor should have a section in their manual which explains what will happen for that case on that version of that compiler with that OS, architecture and libraries, whereas if it's undefined it means the compiler may legitimately do anything that it pleases with no further warning. Classically the GNU compiler has threatened to replace undefined programs with "nethack" or to provide a result binary that when run destroys the undefined source code...)

Nick Lamb
Friday, August 06, 2004

"Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified."

That's it exactly. As with many things in C/C++ it may work just fine on your compiler, but it may not on others.

Incidently, the reason it is not defined is apparently because it's possible for a compiler to optimise better by not worrying about it, so it's even possible that the same compiler could do it differently with different expressions.

Mr Jack
Friday, August 06, 2004

Re-reading my original what I meant to say instead of:
"happens at the end"
was
"happens by the end"

And I thought you were arguing the other end. Particularly with your statement "postincrement means 'increment this and then give me it's original value'" which it clearly doesn't.

What postincrement means is:
"Use the current value and increment at some point between here and the end of this expressions evaluation"

I don't play[1] C language lawyer anymore I've used "end of this expressions evaluation" 'cos I've forgotten what the exact term ANSI use is.

[1] Once upon a time many years ago (1987?) when TurboC was version 1.0 I did the support in the UK and could quote bits of the proposed ANSI spec.

Peter Ibbotson
Friday, August 06, 2004

Good point. My phrasing was sloppy - in my defense I was refering to the original posters usage where the entire expression is 'i++' vs. '++i'.

Mr Jack
Friday, August 06, 2004

Phew... at least we agree. In fact the real problem is in the original article and it's mention of integers.
Also it does depend on architecture, on many processor families there is a post increment / pre decrement addressing mode available to allow stacks to be easily built.

Peter Ibbotson
Friday, August 06, 2004

> An instructor told me years ago that all my loop indexes should be preincremented because it's more efficient.

Does your instructor also cut their lawn with nail scissors?

That's terrible advice. Micro-optimizations do not make programs more efficient.  On modern architecture, that change will save you what, two billionths of a second?  That'll really please the users and sell more copies...

Don't worry a whit about these kinds of optimizations unless you're building a compiler.  Worry about the big stuff that takes milliseconds, not the stuff that takes nanoseconds.

Eric Lippert
Friday, August 06, 2004

comparing:

for ( ; ; i++ ) { ... }

-- and --

for ( ; ; ++i) { ... }

These will likely will generate identical instructions.  The reason is that the only semantic difference is the value of the expression (++i vs i++), and your program doesn't use the value.  If you change it to something like:

int foo;
for ( ; ; foo=i++ ) { ... }

You'll see the difference in the generated code, but they will probably compile to the same number of instructions.
As others have said, the real difference comes when i is not a built-in type, but is an instance of a class that defines the ++ operators.  Then, assuming you are sane and they are semantically identical except for the return value, the postfix operator ++ can't be faster than the prefix operator ++.

EL> That's terrible advice. Micro-optimizations do not make programs more efficient

True, it will almost never make a measurable difference.  But given that one is necessarily no faster than the other, and they are otherwise equivalent, why would you choose the slow one?
I wouldn't go back through existing code just to change this, but for new code why not write ++i?

Brian
Monday, August 09, 2004

*  Recent Topics

*  Fog Creek Home