Let’s say we want to print the numbers from 0 to 9. How do we do that ?
int i=0; for (i=0; i<10; i++) { printf("%d \t", i); }
How about writing the same for loop as below ?
int i=0; for (i=0; i<10; ++i) { printf("%d \t", i); }
Is there any difference between the above two versions of the same loop ? In both the loops, the initialization and condition remain the same. What differs is the way in which we are incrementing the value of “i”. The first for loop uses “post-increment” operator where the operand is suffixed with the operator “i++”. The second for loop uses the “pre-increment” operator where the operand is prefixed with the operator as “++i”.
We need to understand if there is really any difference between the above two versions of a for loop. If there is a difference, then is it in the outcome/correctness or is it in the performance of both the loops ? To answer this question, we first need to understand the difference between “i++” and “++i”.
Well, both “++i” and “i++” increment the value of i. So, if the value of “i” is 4, and we do either a “++i” or “i++” on that value, the resulting value of “i” will be 5 in both the cases. The usage of these two ways becomes important when we do that as part of an expression that yields a result. The following two examples will explain it.
Example 1:
i = 4;
j = i++;
printf(“%d %d”, j, i);
Example 2:
i = 4;
j = ++i;
printf(“%d %d”, j, i);
What will be the output of these two examples ? As mentioned above, it won’t make any difference to the value of “i”. Hence it will be 5 in both the cases. But, that is not the case with the value of “j”. It will be 4 and 5 for example1 and example2 respectively.
++i does an in-place increment on the value of “i”. Hence, the value of “i” is incremented and assigned to “j”. i++ also increments the value of “i”, but it creates a copy of the original value(prior to increment), and this copy gets assigned to “j”. For this reason, usage of pre/post {increment, decrement} operators will make a difference in the outcome/correctness of the expression that yields a result.
Now, back to the for loops. First of all, the output produced by both the for loops will be same. Why ? The reason is that in for loops, there is basically no resulting value out of “i++” and “++i” statements. Both of them will increment the value of “i” after the “printf” statement, and the former one will also create a copy having the previous value. But, does the creation of copy really matter ? The for loop does not care about it, and hence the temporary copy gets thrown away or discarded. Thus, the outcome in both the cases will be values from 0 to 9 both inclusive.
We have understood that correctness of both the loops will not be affected by the usage of “i++” or “++i”. Let’s see if the usage affects the loops in any other manner. As mentioned above, “i++” creates a copy of “i” before incrementing. Does that sound like an additional overhead or a performance penalty ?
The answer is yes. In some cases, the usage really affects the performance of loop. In simple cases like the ones mentioned above, where the value that is being incremented is a primitive integer data type, creating a copy may not be that costly, and we may only see marginal performance gains when using “++i” instead of “i++”. Most importantly, we should not forget that compilers are smart and this small overhead can easily be optimized away by them. Compilers may internally convert “i++” to be “++i”, if they happen to understand that “i” is a primitive integer data type and the conversion of “i++” to “++i” will not make any difference.
However, the compiler will probably not (actually should not) do the same thing when “i” is not a common integer index, but a user defined object type/class which has overloaded the operator ++. When the operator gets overloaded, what “++” really does might be totally different and may not be known or understandable to the compiler. So, the compiler will refrain from doing any sort of optimizations in these cases.
Let’s take an example of a typical “iterator” to a linked list in STL library of C++. If “itr” is an iterator to the list, then for itr++ or ++itr :
Internal implementation of the iterator object would make the iterator point to the next node/element in the list. This will be the responsibility of the functions that implement the “operator overloading” functionality. The function that does overloading for the post-increment operator(itr++) will also make sure to create a copy of the iterator prior to making it point to the next object in the list. Creating a copy of the entire iterator object in such cases can definitely be a huge overhead in loops, and may easily be noticeable if quite a good number of iterations are carried out.
Consider two for loops below where “itr” is an iterator object initialized to the first element in the list.
for(itr = list.begin(); itr != list.end(); itr++) { // Do something with the iterator }
for(itr = list.begin(); itr != list.end(); ++itr) { // Do something with the iterator }
We may notice a substantial performance overhead in the first case. This will be due to continuous construction and destruction of copies of the iterator objects prior to making the iterator point to the next {element, node, object or whatever} in the list. This can be a serious overkill, and so creating copies can really make the performance overhead quite apparent.
Please note that if the compiler happens to optimize away the first loop by converting “itr++” to “++itr”, the outcome will not be affected. Why ? It is because the underlying concept is kind of same. In cases where i as a primitive integer counter/index, i++ will create a copy and then increment i and++i will directly increment i. Similarly, in cases where itr is a user defined object iterator, itr++ will create a copy of the iterator and then make it point to the next object in the data structure. ++itr will directly make the iterator point to the next object in the data structure. Also, ++itr will also help application perform better as we will avoid copies of iterators. But, these semantics may not be the same in case of all user defined objects that overload this operator. Iterator is a very common example of a user defined type, and is much similar in behavior/outcome to a regular integer index. Hence, the compiler optimization (if it happens) will be good.
Consider a case other than standard iterator. May be some user defined type which for some reason overloads “++” operator, and the pre/post increment operator overloading functions do something more or may be an entirely different thing than just the regular ones { direct increment or copy + increment }. In such cases, compiler optimization of “itr++” to “++itr” may actually lead to unexpected results. Hence, the compilers will usually refrain from doing any optimization when the counter variable/iterator is a user defined object type that has overloaded the operator.
My Conclusions:
1. In cases where “i” is as a primitive integer counter/index or a user defined object iterator over a data structure, we should always use the pre-increment(++i) in our loops to avoid the unnecessary performance overhead. In the former case of primitive integer, even if we use post-increment, compilers will mostly perform the optimization on our behalf. But, they won’t do so in cases of iterators or other object types for the reasons mentioned above. Hence, the developer should use the pre-increment operator in their code.
2. In cases where “i” is some other object type and not a standard iterator, we definitely need to understand the semantics of pre/post increment operators and use them accordingly. Again, the compilers won’t touch them.
You can read more about STL iterators and their performance over here.
Reblogged this on Henri Augusto Bisognini.
LikeLike