• Forum
  • Lounge
  • why ++i is better than i++ (when applica

 
why ++i is better than i++ (when applicable)

Pages: 123
Jan 30, 2013 at 3:32am
I have lots of time to think when I drive back and forth to and from school. Today I examined my preference for i++ over ++i when they accomplish the same thing.

I've noticed that for some strange reason, I tend to both type, and think rhythmically. When I program, I sometimes silently say to myself what I am write. One of the main factors,, I presume, in my preference, is that ++i is easier to say. It rolls off the tongue smoother.

Secondly, I think that ++i is easier to type than i++. Well, at least ++somevar. When you are to make the decision to type a variable increment, you know before you start that you will be typing ++. Before you have even fully realized the name of the specific variable, you can have already instinctually typed your ++. I personally have a strange style of typing where I tend to rest my pinky on the shift key in between thoughts / sentences / statements. Probably this comes from the fact that sentences start with capital letters. This means that my pinky usually in a more suited position to begin to type ++ than it will be immediately after typing some identify.

There it is. That is why only fools use i++ in preference of ++i;) And those are the types of pointless thoughts the run through my brain while it sits idle.
Jan 30, 2013 at 4:07am
Except they do different things and there are situations to use one as opposed to the other.
Jan 30, 2013 at 4:31am
In many instances pre increment is faster/ more efficient, i.e. iterators.
Jan 30, 2013 at 4:47am
Except they do different things and there are situations to use one as opposed to the other.

Right, but when they accomplish the same thing...
Jan 30, 2013 at 4:48am
In many instances pre increment is faster/ more efficient, i.e. iterators.

But optimization's should take care of that.
Jan 30, 2013 at 5:05am
But optimization's should take care of that.
Maybe for container implementations where iterators are pointers, but that can't be guaranteed and most (AFAIK) are implemented as class objects.
Jan 30, 2013 at 5:09am
My preference for ++i is simple:

There are some situations where ++i might be a better performer.

There are no situations where i++ will ever be a better performer (unless the operators are overloaded incorrectly or the code is bugged)


Therefore since there's a possible benefit to the prefix form, and no possible benefit to the postfix form, I always prefer the prefix form unless I have specific need of the postfix form.
Last edited on Jan 30, 2013 at 5:09am
Jan 30, 2013 at 5:10am
++Disch;
Jan 30, 2013 at 6:01am
++Disch;
Jan 30, 2013 at 7:08am
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SomeNum
{
    int i;
public:
    SomeNum operator++() // prefix
    {
        i = i + 1;
        return *this;
    }

    SomeNum operator++(int) // postfix
    {
        SomeNum copy(*this);
        i = i + 1;
        return copy;
    }
};


The post-fix operator often makes a copy of the current object. It's simply an extra step which may be expensive if you have a big class.
Last edited on Jan 30, 2013 at 7:13am
Jan 30, 2013 at 12:21pm

There are no situations where i++ will ever be a better performer (unless the operators are overloaded incorrectly or the code is bugged)


Oh, really?

If you don't need the result, a sufficiently intelligent compiler should optimize both to the same machine code.

But if you need the result, i++ is sometimes faster than ++i for primitive types, and I don't know of any compiler that would be able to change the code from ++i to i++ *if you need the result*.

a = b++; // can be processed in 1 CPU cycle, inc and mov executed on different pipelines
a = ++b; // forces RAW and possible pipeline stall

I know they are both doing slightly different thing, but if you can decide, you should always use post-increment where possible (applies to primitive types only).
Last edited on Jan 30, 2013 at 12:26pm
Jan 30, 2013 at 12:37pm
Talking about built-in types, following rapidcoder's example:

Say 'a' is 'eax' and 'b' is 'ebx'.

a = b++; resolves to:
mov eax, ebx
inc ebx


Where
a = ++b; resolves to:
inc ebx
mov eax, ebx


So the operations are the same, but with a different order.
Remember: this ONLY for built-ins.

If you don't need the old return value, you should use ++b anyways. I don't have a single reason why you wouldn't use it in such a situation, except with classes.

Also:
rapidcoder wrote:
forces RAW and possible pipeline stall

What did you mean?

EDIT: Ah, I understand what you mean now, but you may want to explain this for newbie users.
Last edited on Jan 30, 2013 at 12:38pm
Jan 30, 2013 at 1:55pm
Whether to use ++i or i++ depends on the context.

For example for fundamental types there is no any difference between

for ( T i = 0; i < 10; i++ ) { /* ... */ }
and
for ( T i = 0; i < 10; ++i ) { /* ... */ }

where T is some integer type.
Jan 30, 2013 at 2:36pm
a = b++; // can be processed in 1 CPU cycle, inc and mov executed on different pipelines
a = ++b; // forces RAW and possible pipeline stall

I know they are both doing slightly different thing, but if you can decide, you should always use post-increment where possible (applies to primitive types only).


They aren't doing slightly different things. They are doing different things.

Obviously where context matters, you use the one that is appropriate. Where context doesn't matter, it's perfectly efficient to use either in the case of native types and more efficient in many cases to use the prefix version with user-defined types. So, it's reasonable to prefer ++i when context doesn't matter, because the compiler will generate the same code for native types and the most efficient code for user-defined types.

Jan 30, 2013 at 2:43pm
@ rapidcoder: That's a valid point. My understanding of low level pipelinine behavior is admittedly less than I'd like.... but AFAIK it's impossible to accomplish a = ++b without the possible pipeline stall no matter what you do. For example:

1
2
b++;
a = b;


This code will have the same effect (by using the postfix version), and the same read/write, and therefore the same potential pipeline stall.

So it's not that prefix is any slower than postfix. It's just that modifying a variable right before reading it is slower.

So I'm standing by my original statement.

Though I welcome further discussion on the topic. Especially if my understanding is incorrect.
Last edited on Jan 30, 2013 at 2:44pm
Jan 30, 2013 at 2:48pm
Just like with many other things that are 'good habit', I use pre-increment and pre-decrement because it is a 'good habit', as explained in Disch's original post. There are times when I use post-increment or post-decrement, like mymap.erase(it++); because I use the return value of the previous state.

I do have to wonder, in C++11, is there some way to optimize overloaded post-increment/decrement operators? it seems like the making a copy and changing the state is a prime candidate for move constructing but I don't see how it could be easily done without some memory magic.
Last edited on Jan 30, 2013 at 2:49pm
Jan 30, 2013 at 2:50pm
@LB:

mymap.erase(it++);

You should never ever do that. It's very bad. Iterators become invalid once you erase them, and incrementing an invalid iterator is undefined.

This is why erase returns an iterator:

 
it = mymap.erase(it);
Jan 30, 2013 at 2:57pm
Huh? I'm erasing the old iterator from before it got incremented...is the fact that it works for me implementation defined? Or does it have to do with all existing iterators becoming invalid?
Last edited on Jan 30, 2013 at 2:58pm
Jan 30, 2013 at 2:59pm
Disch wrote:
mymap.erase(it++);

You should never ever do that. It's very bad. Iterators become invalid once you erase them, and incrementing an invalid iterator is undefined.


That is actually perfectly valid for containers that don't invalidate iterators to other elements in the container when erase is used. it is advanced before the map element is erased. erase works on a (temporary) copy.

It's good not to be in the habit of doing that though, because it's obviously not safe on all containers.
Jan 30, 2013 at 3:08pm
Ah, yeah. I thought std::map didn't invalidate other iterators besides the one being erased. I'll get out of that habit now.
Last edited on Jan 30, 2013 at 3:08pm
Pages: 123