pre-increment versus post-increment

Started by
13 comments, last by Lactose 7 years, 3 months ago

Hi all,

I'm reading Game Engine Architecture and stumbled upon the topic 'pre-' versus 'post-increment' in for loops.

I.e.:

++i (pre)

i++ (post)

From 'the old days' I remember that I've switched from post to pre, always. At that time is was something about (premature) optimization I recall.

But in the book it says it's better to have post, because 'pre' gives a data dependency, CPU must wait for increment to e completed before it's value can be used in the expression. The book also says thay most compilers will solve it in the background anyway.

Just out of curiosity, what do you do?

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Advertisement
Just out of curiosity, what do you do?

Since this is curiosity rather than anything backed by facts. I always go for i++ unless I specifically need the pre-version. For loops I don't think I have ever used ++i. I use the post version simply because it was the first increment operator I saw/learned and it does the job. I did read a post recently mentioning the pre version and also commenting that the benefit of it no longer applies. How much truth is in that I cannot say. Compilers do tend to be very smart now.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

I use pre because it's algorithmically simpler. In the case of for loops using iterator types instead of integers, for(...; i++) is much more complex than for(...; ++i) (and IMHO - wrong) and often the compiler won't be able to fix that mistake for you, depending on how complex the iterator type is / what your optimization level is.

On the other hand, in the integer for loop case, the compiler is smart enough to switch between i++ and ++i versions in the background for you.
So IMHO, in C/C#/etc, i++ is more typical, but in C++ ++i is the right default.

How is i++ algorithmically more complex than ++i?

If I am not mistaken, they both lead to the same thing when compiled, right?
They should only be two operations. An "ADD" and a "SET"

How is i++ algorithmically more complex than ++i?

If I am not mistaken, they both lead to the same thing when compiled, right?
They should only be two operations. An "ADD" and a "SET"

Good luck with that.

Here's some code from the implementation of std::deque::iterator shipped with one well-known compiler.


 template<typename _Tp, typename _Ref, typename _Ptr>
    struct _Deque_iterator
    {
      typedef _Deque_iterator<_Tp, _Tp&, _Tp*>             iterator;
      typedef _Deque_iterator<_Tp, const _Tp&, const _Tp*> const_iterator;
      typedef _Tp*                                         _Elt_pointer;
      typedef _Tp**                                        _Map_pointer;

      _Elt_pointer _M_cur;
      _Elt_pointer _M_first;
      _Elt_pointer _M_last;
      _Map_pointer _M_node;


      // ...

      _Self&
      operator++()
      {
        ++_M_cur;
        if (_M_cur == _M_last)
        {
          _M_set_node(_M_node + 1);
          _M_cur = _M_first;
        }
        return *this;
      }

      _Self
      operator++(int)
      {
        _Self __tmp = *this;
        ++*this;
        return __tmp;
      }

You can see that the code for pre-incrememnt and post-increment are not the same, and that implementing them in terms of an ADD and a SET is going to be a challenge. In fact, you'll notice the post-increment operator is implemented in terms of the pre-increment operator plus an extra copy of the iterator object itself, which contains four pointers.

The preincrement operator is not always more efficient than the postincrement operator in C++, but when it is, it's the better choice ceteris paribus. Just get in to the habit of always using the preincrement operator and you will not go wrong.

Stephen M. Webb
Professional Free Software Developer

Hi all,

I'm reading Game Engine Architecture and stumbled upon the topic 'pre-' versus 'post-increment' in for loops.

I.e.:

++i (pre)

i++ (post)

From 'the old days' I remember that I've switched from post to pre, always. At that time is was something about (premature) optimization I recall.

But in the book it says it's better to have post, because 'pre' gives a data dependency, CPU must wait for increment to e completed before it's value can be used in the expression. The book also says thay most compilers will solve it in the background anyway.

Just out of curiosity, what do you do?

I use pre by default for the reasons Hodgman described.

With regards to data dependency, it only applies if the result of the increment is used. So using increment at the end of a for loop does not use the result in an expression so there is no data dependency difference. However, if you are using the result of the operator, the the point is marginally valid. However I still don't find it too compelling for the following reasons: 1)the choice between pre and post increment can effect the readability of the algorithm, so this micro-optimization is less important than those considerations. 2) Rewriting code to avoid the use of preincrement may not actually reduce data dependency because the data dependency can be unavoidable. For example, moving a pre-increment to it's own line and making it a post increment does not reduce data dependency.

I'd say that avoiding data dependency is a good idea in designing algorithms in general but using post increment more misses the mark.

As mentioned, from a purist view it should be ++i instead if i++.

Out in the real world...

For integers in this situation there is no difference. All compilers know there is no reason to keep the result around, so there is no difference whatsoever between i++ and ++i in that loop.

For more complex types there may be a difference, since the two operations are likely to do slightly different things. In most cases the difference is still nothing, using one register's value instead of another register's value. In rare cases there will be a non-zero difference.

And even then, in those rare cases where you have a complex type than a pointer or integer where the work is more than just something minor like one register value over another register value, even in those rare cases the difference is almost always a micro-optimization of something so tiny it is dwarfed by things like cache effects, branch predictions, speculative execution, and other fancy things the system does behind your back.

It is a situation where if I happen to notice it I might spend the time to change it, but I know that in the vast majority of situations it is meaningless. Like the choice to turn the doorknob clockwise or counterclockwise, stopping to think about it has a higher cost than just mindlessly taking either action.

I use pre because it's algorithmically simpler. In the case of for loops using iterator types instead of integers, for(...; i++) is much more complex than for(...; ++i) (and IMHO - wrong) and often the compiler won't be able to fix that mistake for you, depending on how complex the iterator type is / what your optimization level is.

On the other hand, in the integer for loop case, the compiler is smart enough to switch between i++ and ++i versions in the background for you.
So IMHO, in C/C#/etc, i++ is more typical, but in C++ ++i is the right default.

The difference between the two operations is that post will return the previous value of the variable you increment when assigning to a variable. In the case of user types, like iterators, this can come with a copy cost and as such the rule started to exist that you should prefer per-increment for a loop variable.

When user types are involved the compiler cannot see the optimisations anymore that it can do for POD types, most likely a ++i and i++ will both result in to a inc eax assembly instruction in the case of a for loop. But when its implemented as an overloaded operator the compiler has to insert that code there.

As a simple example this code with a release compile on VS2015:


int i = 0;
int y = ++i;
int x = i++;
//can be a fair amount of code in between here
std::cout << i << x << y;

Will generate this assembly:

00007FF6718C9C43  mov         edx,2  
00007FF6718C9C48  mov         rcx,qword ptr [__imp_std::cout (07FF6718CD1C0h)]  
00007FF6718C9C4F  call        qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF6718CD198h)] 
00007FF6718C9C55  mov         rcx,rax  
00007FF6718C9C58  mov         edx,1  
00007FF6718C9C5D  call        qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF6718CD198h)]  
00007FF6718C9C63  mov         rcx,rax  
00007FF6718C9C66  mov         edx,1  
00007FF6718C9C6B  call        qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF6718CD198h)]

Which shows that for integers the compiler can compile the increment away completely. When you wrap that code in a for loop you get a sub asm call followed by a jne on the counter value to jump back to the beginning of the loop.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

Thanks guys.
I'll stick with the ++i like I've gotten used to.

To de honest, I think that almost every for loop I use is either an (u)int, size_t or some container's iterator.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Depends on your goal. I see very little post increment usages just for the sake of the "post". So in my opionion it's because of conventions in writing. Loops are written with post incremeant, giving us an habit of writing it in that way (for i=0;i<10;i++).

But when you do need to evaluate the value before incremeanting ( var x = i++) So you both save the previous value and incremeant it at the same time, then you use it.

From readablity perspective it's nonsense to use that. because the purpose of var x=i++ is to assign i to x, the i++ is a side effect although we've directly written it to do it. It's 2 logical operations inside 1 sentence, which is basicly a smelly code to write (like a function with 2 responsibilities).

What was shown above is compiler optimizations. If you provide constant values and do some math with them, it'll simply calculate in compile team.

So (1+2+3+4+5) ++ becomes 16.

It's true with many mathematical operations. The really interesting optimizations are inside the CPU where conditions have to be met.

For example ++i > 5 , How does the cpu know which code to load? The one that ++i > 5 is true or the one that it's smaller?

if i is known as a const, it will produce the value of 6 , then 6>5 is always true. So it would never load the 'else' code.

But if it's a runtime variable, it would sometimes guess wrong (small cases) and will load both code into the cache.

This topic is closed to new replies.

Advertisement