• 0

Merits of using prefix vs postfix increment operator


Question

Conversation split from https://www.neowin.net/forum/topic/1229331-c-need-help-for-converting-roman-numerals-to-decimalinteger/?p=596576971
 

You may already know that pre-increment/decrement is faster than post, because post creates a copy of the original value before incrementing, whereas pre doesn't, and so you should only do post when you actually need to. With the for loop here it may be a little confusing to some about which is needed; this increment operation actually takes place at the end of each loop not the start, so post incrementation is entirely unnecessary. Your compiler may very well optimise this away for you automatically, but personally I think it's better to always explicitly use pre unless you really specially need post.

Even in debug builds any compiler worth its salt will optimize away an unused value like that, but funny things can happen in C++ with overloaded operators on STL iterators and whatnot. As you mention, it's more correct anyway to write the pre-increment. That said I find that in C# it is basically a matter of style and I don't try to impose this on others (as I do with many other nagging details :p).

Link to comment
Share on other sites

Recommended Posts

  • 0

@OP. Understand that your code is walking through the string interpreting each character one at a time entirely independently of any other characters around it. As you can see, this means that you get some incorrect results.

 

Recognising that the order of characters can affect their individual meaning from addition to subtraction, it is clear that your current algorithm is insufficient to correctly interpret strings. Put the code to one side. First go and make sure you understand the exact correct rules for correctly formatted roman numerals. Second, think logically about how to approach parsing a string of them to correctly interpret value. It could require perhaps a loop to go over each character, with a nested loop with which for each character you look ahead at all the following characters to determine if there is a larger valued characters following it. Or instead of just searching for single digits, you could expand to searching for matching multi-digit pieces as demonstrated above. Perhaps it could help if you examined the string from right to left (backwards) instead of left to right. (I'm not certain which of these may actually be helpful, I haven't put much thought towards a solution to this myself, I'm mearly throwing suggestions at you of things to consider that you might not have thought of).

 

 

You lost me at z = -1, did you mean x = -1?? :p

 

I'm going to be cheeky and take the opportunity to throw a little optimisation tip at you, just in case you don't yet know it:

Instead of

for (x=0; x<y; x++) {

write

for (x=0; x<y; ++x) {

You may already know that pre-increment/decrement is faster than post, because post creates a copy of the original value before incrementing, whereas pre doesn't, and so you should only do post when you actually need to. With the for loop here it may be a little confusing to some about which is needed; this increment operation actually takes place at the end of each loop not the start, so post incrementation is entirely unnecessary. Your compiler may very well optimise this away for you automatically, but personally I think it's better to always explicitly use pre unless you really specially need post.

Haha I meant x = -1 to restart the loop ;)

 

And never knew of ++x, will look into it, thank!

Link to comment
Share on other sites

  • 0

I'm going to be cheeky and take the opportunity to throw a little optimisation tip at you, just in case you don't yet know it:

Instead of

 

for (x=0; x<y; x++) {
write

for (x=0; x<y; ++x) {
You may already know that pre-increment/decrement is faster than post, because post creates a copy of the original value before incrementing, whereas pre doesn't, and so you should only do post when you actually need to. With the for loop here it may be a little confusing to some about which is needed; this increment operation actually takes place at the end of each loop not the start, so post incrementation is entirely unnecessary. Your compiler may very well optimise this away for you automatically, but personally I think it's better to always explicitly use pre unless you really specially need post.
It's a bad idea trying to second guess the compiler. You're assuming the compiler doesn't know how best to arrange its own symbol tree. It's also premature optimisation. Donald Knuth said it best:

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

If you're really concerned about that degree of efficiency, you should be writing it in assembly. That way, you can control the execution precisely without things getting lost in translation.

Link to comment
Share on other sites

  • 0

It's a bad idea trying to second guess the compiler. You're assuming the compiler doesn't know how best to arrange its own symbol tree. It's also premature optimisation. Donald Knuth said it best:

If you're really concerned about that degree of efficiency, you should be writing it in assembly. That way, you can control the execution precisely without things getting lost in translation.

 

Let's please try not to drag this too far off topic. Perhaps a mod could break this discussion off into a separate thread?  - done, Andre S.

 

I completely disagree with your assessment of my suggestion to use ++i insteadof i++. Have a read of this: http://herbsutter.com/2013/05/13/gotw-2-solution-temporary-objects/

 

The relevant portions:

 

 

Definition: Premature optimization is when you make code more complex in the name of efficiency without data that it?s actually needed.

 

?But wait, you?re being inconsistent!? I can just hear someone saying. ?That?s premature optimization. You said that compilers can hoist the end() call out of the loop, and it?s just as easy for a compiler to optimize away this postincrement temporary.?

That?s true, but it doesn?t imply premature optimization. Preferring ++i does not mean writing more complex code in the name of performance before you can prove it?s needed?++i is not more complex than i++, so it?s not as if you need performance data to justify using it! Rather, preferring ++i is avoiding premature pessimization, which means avoiding writing equivalently complex code that needlessly asks for extra work that it?s just going to ignore anyway.

Definition: Premature pessimization is when you write code that is slower than it needs to be, usually by asking for unnecessary extra work, when equivalently complex code would be faster and should just naturally flow out of your fingers.

 

I'm sorry but your argument that I should be writing in assembly just because I care about little bits of greater efficiency that can easily be achieved with simple adjustments, such as typing the correct damn increment operation here, is frankly ridiculous.

Link to comment
Share on other sites

  • 0

Let's please try not to drag this too far off topic. Perhaps a mod could break this discussion off into a separate thread?

 

I completely disagree with your assessment of my suggestion to use ++i insteadof i++. Have a read of this: http://herbsutter.com/2013/05/13/gotw-2-solution-temporary-objects/

I'm sorry but your argument that I should be writing in assembly just because I care about little bits of greater efficiency that can easily be achieved with simple adjustments, such as typing the correct damn increment operation here, is frankly ridiculous.

My point is, it's quite irrelevant in most cases like the code in this thread. More often than not, a modern compiler will simply discard the original value of i because it's not even referenced. Again, this is a premature optimisation which will most likely produce the exact same machine code as a postincrement operator. For integral types, my advice would be to maintain consistency with your development team.

This article makes a good case for why it's simply a distraction that programmers should shun:

http://scientopia.org/blogs/goodmath/2011/05/03/the-perils-of-premature-optimization/

Unless it's causing a bottleneck in a program, I'd recommend against doing it.

Link to comment
Share on other sites

  • 0

My point is, it's quite irrelevant in most cases like the code in this thread. More often than not, a modern compiler will simply discard the original value of i because it's not even referenced. Again, this is a premature optimisation which will most likely produce the exact same machine code as a postincrement operator. For integral types, my advice would be to maintain consistency with your development team.

This article makes a good case for why it's simply a distraction that programmers should shun:

http://scientopia.org/blogs/goodmath/2011/05/03/the-perils-of-premature-optimization/

Unless it's causing a bottleneck in a program, I'd recommend against doing it.

 

For the code in this thread, yes it really doesn't matter which is used, I agree. The reason I brought it up was simply to take the opportunity to point out to Seahorsepip that there are two similar but different operators, intended for slightly different things, and that pre-incrementation is more correct in that particular usage scenario, partly yes just out of being pedantic about correctness, but also just in case he/she didn't know this, which it turns out he/she did not, in order to ensure that he/she may in future be in a position to consciously make an informed decision on which to use if and when it may actually matter.

 

If you were to simply argue that it's generally an unnecessary optimisation, like Andre did, because being something so simple for a compiler to optimise (with integral types), you can guarantee that any modern optimising compiler will do it, then I wouldn't really bother disputing that, hence I felt it unnecessary to reply to Andre. (Actually I just now notice that he said even in debug builds, and checking that out I see that he is indeed right on that point; even with debug builds with optimisation disabled my VS compiler still optimises this, which is nice).

 

What particularly irks me however is your insistence in calling it premature optimisation. As per the definition I quoted above, premature optimisation is making code more complex in the name of efficiency, without proof that it's actually making a positive difference. Writing ++i is no more complex than writing i++, so that does not fall under premature optimisation. Writing i++ instead of ++i, where i++ is not specifically necessary, is however lazy and falls under the also quoted definition of premature pessimization (which I won't bother repeating, just see above), because you're writing equivalently complex code but asking for unnecessary extra work. The fact that the compiler optimises the extra work away for you in this case is irrelevant, you're still asking for it by not using the best tool for what you required.

 

So in other words unless a bottleneck is being created, you'd advocate lazy, careless programming over, how should I put it, perfectionism and correctness. An attitude that I would suggest reflects a programmer likely to produce buggier code than someone equivalently experienced but more strict and pedantic in the way they approach designing and writing code.

 

I find the notion of consciously avoiding pre-incrementation on the basis of consistency with other developers in a team absolutely ridiculous.

 

As you seem to concede along with Andre, with non-integral types, there's no guarantee that the two operators are interchangeable; the compiler must work harder to ensure that this is the case if it is to swap a post-inc with a pre-inc for optimisation purposes. Why would you lazily force it to go through this instead of just using the correct operator in the first place. If you're going to concede that using the correct operator for non-integral types is a good idea, then why not also do so with integral types, in the interest of consistency, where it actually makes some decent sense to be consistent.

 

I read the article you pointed me towards. It may perhaps surprise you to know that I do actually pretty much entirely agree with it, it's not that bad. I do already agree that the concept of premature optimisation holds a lot of merit, same goes for the discussion specifically regarding inlining. However if you look at his discussion of pre/post increment, he's arguing against programmers who insist that pre is always faster, who ignore the fact that decent compilers will optimise the inefficiency away. He's not arguing that it's not more correct to use pre, nor is he arguing that it is not a good idea to use pre by default based on other arguments. He also does not say that it is never faster to use pre - what if you're forced to use a crappy compiler for something, if you actually understand the difference and you're used to using pre, you're more likely to produce a slightly better program than otherwise; also a good idea to use pre by default if there's any chance your code could at any time be compiled with a crappy compiler at any point in the future, perhaps by other developers, and it's a good idea to lead by example, teaching new developers the good practice of using the correct operator.

 

Ultimately I'm a perfectionist, and I like to adhere to strict correctness as much as possible, so we may have to just leave it that we agree to disagree on this.

Link to comment
Share on other sites

  • 0

The use of the post-increment operator is also misleading to beginners. I see a lot of dubious code like

return i++; // why would you increment i *after* returning from the method? if i is local it makes no sense, if not it's still dubious
if (i++ % 10 == 0) // did the author really intend to compare with the *original* value of i?

Even expert programmers get it wrong sometimes because they're not paying attention. If people defaulted to pre-increment instead of post-increment (which is in fact very rarely useful), it would eliminate these types of errors.

 

And of course in languages not derived from C these strange operators do not exist, i.e. you just do

i = i + 1

and there's no possible confusion.

  • Like 1
Link to comment
Share on other sites

  • 0

The use of the post-increment operator is also misleading to beginners. I see a lot of dubious code like

return i++; // why would you increment i *after* returning from the method? if i is local it makes no sense, if not it's still dubious
if (i++ % 10 == 0) // did the author really intend to compare with the *original* value of i?

To be honest, code like that but using the pre-increment operator is still misleading, harder to read and most of the time just written because it makes the author feel good about himself (Yeah, I can write code in one line that is almost unreadable but it looks cool!) :p

 

Most of the time writing the increment on the line above gives a lot better view of what the author was trying to accomplish. And the compiler will probably do the exact same thing with it anyway.

Link to comment
Share on other sites

  • 0

While I agree that it's important to know the distinction between pre and post increment operators and their effects, in the case of a for loop, it's really quite irrelevant one way or the other.

For instance, even with optimisations disabled, the GCC compiler produces the exact same assembly/machine code for both pre and post increment in a for loop construct:

void
main ( void ) {
	int i;
	for ( i = 0; i < 10; ++i ); 
}    
$ gcc -O0 -S test.c
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$0, -4(%rbp)      ; i = 0
	jmp	.L2
.L3:
	addl	$1, -4(%rbp)      ; ++i or i++ always results in a single cpu instruction 'addl source, dest'
.L2:
	cmpl	$9, -4(%rbp)      ; i < 10
	jle	.L3
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
Link to comment
Share on other sites

  • 0

I'm sorry but your argument that I should be writing in assembly just because I care about little bits of greater efficiency that can easily be achieved with simple adjustments, such as typing the correct damn increment operation here, is frankly ridiculous.

 

I don't think so.

 

Code readability is very important and sadly overlooked way too often. I've seen my fair share of "optimized code" that was impossible to understand without pulling out your hairs. The worst is once your "un-optimize" the code (i've done it often) to make it readable the change in performance is not perceptible.

 

In a case where i++ or ++i doesn't change the behaviour of the application unless you are making a 3d engine or some complex algorithms you should not ask yourself which of i++ or ++i will be faster but which one makes your code easier to read. Don't be clever. Be understandable.

 

I tend to agree that if you need to that kind of optimization better code this part of the application in Assembler. Unless i'm mistaken part of the 3d engines are still done in Assembler.

 

if (i++ % 10 == 0)

I would fire any dev writing something like this and not commenting why he did it.

Honestly i rarely use i++ or ++i outside of a loop. In a case like the one above i would probably increment i before or after the if (depending on what i want to achieve) to avoid any possible confusion. The resulting machine code will be the same anyway unless the compiler fails badly. I'll have 2 lines of code instead of one but future devs working with my code will quickly and clearly understand what i want to do without having to "read" the whole thing or debug to be sure i wanted to compare the original value.

Link to comment
Share on other sites

  • 0

I think it is important to get into the habit of using the pre-increment operator, because it would be important to use this when using memory-allocating iterators.

 

For example, say you have an iterator (e.g. boost::filter_iterator) that is predicated using a predicate object that needs to allocate memory (a boost::function needs to under certain circumstances), it is important to use the pre-increment operator as it avoids copying the iterator which may lead to memory allocations. As a memory allocation occurs when the iterator is copied it is very important to avoid copying it (in embedded systems and very long running allocations the fragmentation caused by unpooled allocations can lead to out-of-memory errors even if a significant amount of memory is still available).

 

In C++11 it was clarified that memory allocations can be optimised away if they are redundant, but that doesn't mean that compilers will do so. A C++98 or 03 compiler may not even legally be able to do so.

 

So to keep consistent with the style of other loops and to generate the best possible code on older compilers "++i" should be used.

Link to comment
Share on other sites

  • 0

9. Don't pessimize prematurely.

...

Discussion

Avoiding premature optimization does not imply gratuitously hurting efficiency. By premature pessimization we mean writing such gratuitous potential inefficiencies as:

  • ...
  • Using postfix ++ when the prefix version is just as good. (see Item 28.)
  • ...

28. Prefer the canonical form of ++ and --. Prefer calling the prefix forms

Summary

...

Prefer to call the prefix versions if you don't need the original value.

(C++ Coding Standards, Sutter & Alexandrescu).

Everyone who studies a C-based programming language finds out that ++ and -- come in prefix and postfix forms early on, so I don't think there's any argument of readability in the two. Prefix is just as readable as postfix.

Furthermore, simpler compilers may benefit from the prefix form. The prefix form also has less side potential side effects and is, in 90% of cases, the intended form that the programmer wanted to use.

Prefix > postfix, unless you need the specific "return the original" feature of postfix.

  • Like 2
Link to comment
Share on other sites

  • 0

To be precise, my C++ text book dedicates a section to pre and post increment in chapter three, even before discussing functions.

Link to comment
Share on other sites

  • 0

(C++ Coding Standards, Sutter & Alexandrescu).

Everyone who studies a C-based programming language finds out that ++ and -- come in prefix and postfix forms early on, so I don't think there's any argument of readability in the two. Prefix is just as readable as postfix.

Premature optimisation isn't just about readability and complexity, though both of those are valid concerns. It's also the question of "does this really improve the performance of the code, and by how much". If someone can't answer that simple question precisely, then they have no business doing it. Suggesting that others adopt that same practise for the sake of some imaginary speed increase at the expensive of consistency with existing code, other people's work, or just the distraction alone, is a disaster waiting to happen. It teaches the bad habit of prematurely optimising code without knowledge of its effects, and simply relying on blind faith and hearsay.

 

Furthermore, simpler compilers may benefit from the prefix form.

I doubt you'll find a modern compiler that doesn't use a single register for both forms. It's the standard form in most textbooks and compiler developers design them with that in mind. The supposed benefit of this form is questionable at best, and complete fabrication at worst.

 

The prefix form also has less side potential side effects and is, in 90% of cases, the intended form that the programmer wanted to use.

It's far better for the programmer to actually spend the necessary time to learn the difference and to then be able to make an informed choice. If 'i++' in a for loop is good enough for the creators of the C language (K&R), then it's good enough for me. There's truthfully better things to spend one's time on than such trivialities which have no tangible impact.
Link to comment
Share on other sites

  • 0

I prefer to use post-increment, I just prefer the notation and it feels more natural using it.

 

The only time I use pre-increments is if it's a high performance application where minimal performance increases/hits matter, and I'm actually using the value in i (as opposed to just using it as an incriminator). For example, in the following loops:

 

 

for (i=0, i<10000000, i++)
{
   // Do something unrelated to i, e.g.:
   print "hello";
}
and

 

for (i=0, i<10000000, ++i)
{
   // Do something unrelated to i, e.g.:
   print "hello";
}
Performance (using most modern compilers) will be identical. In the loops:

 

for (i=0, i<10000000, i++)
{
   // Do something related to i, e.g.:
   print i;
}
and

 

for (i=0, i<10000000, ++i)
{
   // Do something related to i, e.g.:
   print i;
}
The pre-increment should be faster. In post-increment, the old value has to be stored for the duration of the loop, which requires memory operations. This might not matter if you're doing this a few thousand times, but if we're talking about billions of operations, or > n^3/n^4 complexity, it soon adds up.
Link to comment
Share on other sites

  • 0

Premature optimisation isn't just about readability and complexity, though both of those are valid concerns. It's also the question of "does this really improve the performance of the code, and by how much". If someone can't answer that simple question precisely, then they have no business doing it. Suggesting that others adopt that same practise for the sake of some imaginary speed increase at the expensive of consistency with existing code, other people's work, or just the distraction alone, is a disaster waiting to happen. It teaches the bad habit of prematurely optimising code without knowledge of its effects, and simply relying on blind faith and hearsay.

Note that I didn't mention premature optimization, I mentioned premature pessimization. Premature pessimization is the act of deliberately choosing a potentially less suitable option for no tangible benefit.

Pre-increment vs post-increment are two entirely different operations. Performance considerations aside, the two have entirely different purposes within code:

Pre-increment (++i): Increment the variable and return the new value (old value: 0, new value: 1, returned value: 1).

Post-increment (i++): Increment the variable and return the original value (old value: 0, new value: 1, returned value: 0).

Using pre-increment where post-increment is desirable can introduce very nasty bugs into code. Consider the following:

Example #1:

for (int i = 0; i < 5; i++)
{
    printf("Programming is fun\n");
}

Example #2:

int i = 0;
while (i++ < 5)
{
    printf("Programming is fun\n");
}

Two loops, both using post increment. The first loop iterates four times, the second loop iterates five times. To make the loops equivalent, pre-increment is the better choice here.

 

I doubt you'll find a modern compiler that doesn't use a single register for both forms. It's the standard form in most textbooks and compiler developers design them with that in mind. The supposed benefit of this form is questionable at best, and complete fabrication at worst.

If you want to use post-increment in for loops or in isolation and assume the compiler will make such optimizations, fine. I'd still argue that you get better consistency from using pre-increment though.

 

It's far better for the programmer to actually spend the necessary time to learn the difference and to then be able to make an informed choice. If 'i++' in a for loop is good enough for the creators of the C language (K&R), then it's good enough for me. There's truthfully better things to spend one's time on than such trivialities which have no tangible impact.

I agree entirely, and the informed choice in the majority of cases is to use a pre-increment. At worst you have code that is inconsistent with text books, and at best you get a minor optimization in tight loops, better declaration of intent and less chance of off-by-one errors. There are few scenarios where the return value of an increment is useful, and even fewer where the post-increment return value is useful.

The idea that the two are interchangeable outside of trivial examples like for loops is toxic. Newbies that assume this to be the case can easily fall into traps like:

 

int i = 0;
int j = ++i; // j == 1.

int x = 0;
int y = x++; // y == 0.
Link to comment
Share on other sites

  • 0

The idea that the two are interchangeable outside of trivial examples like for loops is toxic. Newbies that assume this to be the case can easily fall into traps like:

 

int i = 0;
int j = ++i; // j == 1.

int x = 0;
int y = x++; // y == 0.

 

this was one of the first things i learned in Java, just before i learned C and C++ and to use post increment non stop because that's how my teacher told us; in Java i learned how wrong i was and how i could use pre or post increment in a efficient way.

 

Now i strive for people to do more readable code (not jamming everything and the kitchen sink into a oneliner).

Link to comment
Share on other sites

  • 0
for (i=0, i<10000000, ++i)
{
   // Do something related to i, e.g.:
   print i;
}
The pre-increment should be faster. In post-increment, the old value has to be stored for the duration of the loop, which requires memory operations. This might not matter if you're doing this a few thousand times, but if we're talking about billions of operations, or > n^3/n^4 complexity, it soon adds up.
I just tried it, and the assembly for both is identical.

 

void
main ( void )
{
	int i, x;
	for ( i = 0; i < 10; ++i )
		x = i; 
} 
gcc -O0 -S test.c
.LFB0:
	.cfi_startproc
	pushq	%rbp             ; stack pointer ( -4 = i, -8 = x )
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq    %rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$0, -4(%rbp)     ; i = 0
	jmp	.L2
.L3:                             ; performed each iteration
	movl	-4(%rbp), %eax   ; save i in eax
	movl	%eax, -8(%rbp)   ; copy eax to x
	addl	$1, -4(%rbp)     ; increment i counter
.L2:
	cmpl	$9, -4(%rbp)     ; i < 10
	jle	.L3              ; repeat
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
This is the problem with premature optimisation. Changing your code based upon assumptions about how the compiler works is a dangerous occupation.
Link to comment
Share on other sites

  • 0

FYI, in C#, i++ and ++I are identical.

        private void test1()
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("Meow " + i.ToString());
            }
        }

        private void test2()
        {
            for (int i = 0; i < 10; ++i)
            {
                Console.WriteLine("Meow " + i.ToString());
            }
        }

Decompiled, it's identical (I only posted the first one)

.method private hidebysig instance void  test1() cil managed
{
  // Code size       44 (0x2c)
  .maxstack  2
  .locals init ([0] int32 i,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  br.s       IL_0022
  IL_0005:  nop
  IL_0006:  ldstr      "Meow "
  IL_000b:  ldloca.s   i
  IL_000d:  call       instance string [mscorlib]System.Int32::ToString()
  IL_0012:  call       string [mscorlib]System.String::Concat(string,
                                                              string)
  IL_0017:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001c:  nop
  IL_001d:  nop
  IL_001e:  ldloc.0
  IL_001f:  ldc.i4.1
  IL_0020:  add
  IL_0021:  stloc.0
  IL_0022:  ldloc.0
  IL_0023:  ldc.i4.s   10
  IL_0025:  clt
  IL_0027:  stloc.1
  IL_0028:  ldloc.1
  IL_0029:  brtrue.s   IL_0005
  IL_002b:  ret
} // end of method Program::test1


Link to comment
Share on other sites

  • 0

I guess the question is really about using the post-increment in the context of a for loop using an integer as an index, i.e. the classical

for (int i = 0; i < array_length; i++)

because that's the one case where it's common to see a post-increment and it doesn't really matter in terms of correctness or performance. I don't think we need more assembly examples or test programs to convince us of this fact.

 

The point raised by many and which I share, is that although it may not matter in this case, it does in general, and in general the pre-increment is what you want, as has been amply illustrated. So there's an argument from consistency to be made: if you're to use one by default, it might as well be pre-increment.

 

It's also just more logical to use the pre-increment in this case, since you're not interested in the value of the expression, a fortiori you're not interested in the original value of i, so why specifically use post-increment?

 

The argument that certain books or documentation prefer one form goes both ways; long-time C++ programmers (like myself), familiar with STL iterators, know that the most correct form with and without optimisation is

for (container::iterator it = collection.begin(); it != collection.end(); ++it)

although C++11 changes the syntax somewhat, but overloaded operators can still run arbitrary code and calling the right one still matters a lot in this language. I observe that among C# programmers, those who favor the pre-increment are often those who come from a C++ background.

 

The argument from habit also goes both ways, personally I don't even think about writing ++i, it's just second nature now.

 

So basically the argument that I could see in favor of using the post-increment in for loops is that it still probably the most commonly seen in documentation or open-source code. Even MSDN in its C# reference always writes for loops this way (example). However, it's not that clear-cut: if we're talking C#, Roslyn (the new C# compiler), which is a gem of modern design and should serve as model for state-of-art programming, adopts the pre-increment form (example).

 

Given all of this I believe the case for post-increment is very weak, there's simply not much to be said in its favor. I would personally not insist much on its adoption (as there are more pressing issues with our coding guidelines IMO), but if one style is to be adopted then it manifestly should be the pre-increment.

  • Like 1
Link to comment
Share on other sites

  • 0

I guess the question is really about using the post-increment in the context of a for loop using an integer as an index, i.e. the classical

for (int i = 0; i < array_length; i++)
because that's the one case where it's common to see a post-increment and it doesn't really matter in terms of correctness or performance. I don't think we need more assembly examples or test programs to convince us of this fact.

 

The point raised by many and which I share, is that although it may not matter in this case, it does in general, and in general the pre-increment is what you want, as has been amply illustrated. So there's an argument from consistency to be made: if you're to use one by default, it might as well be pre-increment.

I don't think you can generalise with something like increment operators. A case by case basis is required. So saying you should always default to pre-increment seems like bad advice. In a for loop construct context, it doesn't matter whether you use pre or post with the iterator. Both forms evidently result in the same machine code, as the posts above prove. Unless a piece of code proves to be a bottleneck, it's always better to stick with the standardised version and maintain consistency.

 

It's also just more logical to use the pre-increment in this case, since you're not interested in the value of the expression, a fortiori you're not interested in the original value of i, so why specifically use post-increment?

Of course you're interested in the original value of 'i', otherwise you wouldn't be using a for loop in the first place. The most common reason for using that construct is that you already know how many times it's going to execute, and you're enumerating some kind of array or collection. If you look at how the assembly works, you'll see that when 'i' is accessed in the loop body, it's saved to a register in assembly (movl -4(%rbp), %eax). That is the original value. You're going to access it regardless of whether you use a pre or post increment iterator. Only once the body has finished does the iterator expression execute. At which point, it has no effect either way because the stack memory simply gets incremented by 1 (addl $1, -4(%rbp)). The original value is saved in the register provided the loop body accessed it, and the incrementation is performed on the stack.

 

The argument that certain books or documentation prefer one form goes both ways; long-time C++ programmers (like myself), familiar with STL iterators, know that the most correct form with and without optimisation is

for (container::iterator it = collection.begin(); it != collection.end(); ++it)
I don't do C++ myself, but have you checked whether or not the assembly differs between the two? I have a feeling it will work the same as integral types.

 

The argument from habit also goes both ways, personally I don't even think about writing ++i, it's just second nature now.

If someone wants to do it that way for stylistic reasons, I've got no problem with it. However, evangelising the form and trying to convince others to adopt it on the false premise of performance gains is where I draw the line. Because that's promoting premature optimisation.

 

However, it's not that clear-cut: if we're talking C#, Roslyn (the new C# compiler), which is a gem of modern design and should serve as model for state-of-art programming, adopts the pre-increment form (example).

Actually, if you look elsewhere, it has post-increment as well. Clearly Microsoft has multiple programmers working on it. I'm surprised it doesn't dictate a common form though. Having a mixture of both is inconsistent and confusing.

 

Given all of this I believe the case for post-increment is very weak, there's simply not much to be said in its favor. I would personally not insist much on its adoption (as there are more pressing issues with our coding guidelines IMO), but if one style is to be adopted then it manifestly should be the pre-increment.

It's certainly at the bottom of the list when it comes to priorities. As you say, there are more pressing concerns. All in all, it's been an interesting discussion. It's nice to hear the various opinions on the matter.
Link to comment
Share on other sites

  • 0

I don't think you can generalise with something like increment operators. A case by case basis is required. So saying you should always default to pre-increment seems like bad advice.

Perhaps a better way of putting it is that ++i is shorthand for i += 1 or i = i + 1; i++ isn't because unlike all the other forms, it evaluates to the original value of i.

 

We can see that programmers commonly use i++ as shorthand for i += 1; or i = i + 1;. This is incorrect in the general case and only correct in specific cases where 1) the value of the expression is unimportant and 2) the copy can be optimized away. Therefore it's the wrong default.

 

Note that by default I don't mean something that should always be used without thinking, but an extra rule to disambiguate between otherwise equivalent options.

 

I don't do C++ myself, but have you checked whether or not the assembly differs between the two? I have a feeling it will work the same as integral types.

 

It doesn't, and when you do multiplatform C++ with unreliable compilers like game developers doing PS3, Nintendo DS, etc., you quickly learn not to rely on compiler optimizations or inlining heuristics. So yes it does matter from a performance standpoint in certain cases, and you don't always know in advance, and don't want to rely on that knowledge for your code to be optimal anyway.

  • Like 1
Link to comment
Share on other sites

  • 0

Perhaps a better way of putting it is that ++i is shorthand for i += 1 or i = i + 1; i++ isn't because unlike all the other forms, it evaluates to the original value of i.

Only as part of another expression. In isolation, (++i) and (i++) both effectively equal i += 1 or i = i + 1. The assembly demonstrates that in the for loop. A compiler will generate a single increment cpu instruction that's performed on the stack - addl $1, -4(%rbp). It doesn't evaluate to the original value and no additional instructions are performed unless the programmer explicitly avails himself of that feature by including it as part of a larger expression.

In the for loop construct for example, executing (;; i++ ) in the iterator means the expression is performed in isolation after the loop body has completed. The only way the original value can be saved outside the loop body, is if the programmer explicitly does so - (;; x = i++ ). So you see, ++i will never be faster because only a single addl $1, -4(%rbp) will ever be performed when the iterator is incremented in isolation.

I've yet to see a single compiler that would ever save the original value of 'i' if it's never even used. And even if it did, in a for loop context, 'i' is most probably accessed in the body anyway, which means it's already loaded in a register (%eax). I honestly see no justification for a general recommendation of pre-increment. Either the programmer executes the incrementation in isolation, which renders the point moot, or he uses it as part of a larger expression, in which case intent should dictate the form.

 

We can see that programmers commonly use i++ as shorthand for i += 1; or i = i + 1;. This is incorrect in the general case and only correct in specific cases where 1) the value of the expression is unimportant and 2) the copy can be optimized away. Therefore it's the wrong default.

Specific cases? I'd say 1) is the most common case. I use it myself all the time. And because it occurs in isolation, just as the for loop does, it requires only a single cpu instruction (addl). If the programmer intends to employ it in a more complex expression, I'd probably advise against it anyway because a) it makes the code harder to read, and b) it's more likely to cause side effects if he doesn't fully understand the features of each operator. Again, it comes back to intent. One must be mindful of this if an increment operator isn't used in isolation. And that applies to both pre and post increment. That's why I don't believe it can be generalised in that context.

 

It doesn't, and when you do multiplatform C++ with unreliable compilers like game developers doing PS3, Nintendo DS, etc., you quickly learn not to rely on compiler optimizations or inlining heuristics. So yes it does matter from a performance standpoint in certain cases, and you don't always know in advance, and don't want to rely on that knowledge for your code to be optimal anyway.

I don't think it's unreasonable to ask the compiler to do its job. I'd be surprised if a compiler produced a different result for both a pre and post increment iterator. C++'s STL might be an exception. I'd imagine that in languages like C#, Java, and Python, there's absolutely no difference.

As can be seen thus far in this thread, compilers don't even see this as part of the optimisation process. It's integral to the design.

Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.