Is it reasonable to use the prefix increment operator ++it instead of postfix operator it++ for iterators?


Published on

I decided to find out if there is practical sense in writing ++iterator instead of iterator++ when handling iterators. My interest in this question arouse far not from my love to art but from practical reasons. We have intended for a long time to develop PVS-Studio not only in the direction of error search but in the direction of prompting tips on code optimization. A message telling you that you'd better write ++iterator is quite suitable in the scope of optimization.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Is it reasonable to use the prefix increment operator ++it instead of postfix operator it++ for iterators?

  1. 1. Is it reasonable to use the prefixincrement operator ++it instead ofpostfix operator it++ for iterators?Author: Andrey KarpovDate: 13.02.2011I decided to find out if there is practical sense in writing ++iterator instead of iterator++ when handlingiterators. My interest in this question arouse far not from my love to art but from practical reasons. Wehave intended for a long time to develop PVS-Studio not only in the direction of error search but in thedirection of prompting tips on code optimization. A message telling you that youd better write++iterator is quite suitable in the scope of optimization.But how much relevant is this recommendation nowadays? In ancient times, for instance, it was advisednot to repeat calculations. It was a good manner to write:TMP = A + 10;X = TMP + B;Y = TMP + C;instead ofX = A + 10 + B;Y = A + 10 + C;Such subtle manual optimization is meaningless now. The compiler would handle this task as well. Itsjust unnecessary complication of code.Note for pedantic ones. Yes, youd better not repeat calculations and calculate long expressions, whichare used several times, separately. What Im talking about is that there is no reason in optimizing simplecases like the one I have mentioned.Well, we have digressed from our main point which is the question if the advice to use the prefixincrement instead of postfix increment for operators is obsolete nowadays; if we should store our mindwith one more subtle thing. Perhaps the compiler learned to optimize prefix increments long ago.A bit of theory at first for those who are not familiar with the topic. All the rest may scroll the text a bit.The prefix increment operator changes an objects state and returns itself in the changed form. Theprefix increment operator in the iterator class to handle std::vector may look this way:_Myt& operator++(){ // preincrement ++_Myptr;
  2. 2. return (*this);}The situation with the postfix increment is more complicated. The objects state must change but it isthe previous state which is returned. An additional temporary object is created:_Myt operator++(int){ // postincrement _Myt _Tmp = *this; ++*this; return (_Tmp);}If we want to just increment the iterators value, it turns out that the prefix operator is more preferable.That is why, here you are one of the tips concerning software micro-optimization: write "for (it =a.begin(); it != a.end; ++it)" instead of "for (it = a.begin(); it != a.end; it++)". In the latter case, anunnecessary temporary object is created, which reduces performance.You may read about all this in detail in the book by Scott Meyers "Efficient use of C++. 35 newrecommendations on improving your programs and projects" (Rule 6. Distinguish between prefixincrement and decrement operators) [1].The theory is over. Now practice. Is there sense in replacing the postfix increment with the prefix one incode?size_t Foo(const std::vector<size_t> &arr){ size_t sum = 0; std::vector<size_t>::const_iterator it; for (it = arr.begin(); it != arr.end(); it++) sum += *it; return sum;}I know that we may wander into the depths of philosophy now. Say, it may turn out that some otherclass would become the container instead of vector and iterators in this new class would be verycomplex and heavy; when copying the iterator, we would have to establish a new connection to thedatabase and so on. So, you must always write ++it.But its theory. But in practice, when we encounter such a loop in our code, is it reasonable to replaceit++ with ++it? Cannot we rely on the compiler that will guess itself that it can throw away anunnecessary iterator?
  3. 3. The answers are strange but the reason why we give them will become evident through furtherexperiments.Yes, we must replace it++ with ++it.Yes, the compiler will optimize the code and it wont matter what increment we use.I chose an "average compiler" and created a test project for Visual Studio 2008. It has two functionswhich calculate the sum using it++ and ++it and also estimates their running time. You may downloadthe project here. Here is the code of functions the speed of which was measured:1) Postfix increment. iterator++.std::vector<size_t>::const_iterator it;for (it = arr.begin(); it != arr.end(); it++) sum += *it;2) Prefix increment. ++iterator.std::vector<size_t>::const_iterator it;for (it = arr.begin(); it != arr.end(); ++it) sum += *it;Working time in the Release version:iterator++. Total time : 0.87779++iterator. Total time : 0.87753This is the answer to the question if the compiler can optimize the postfix increment. Sure it can. If youstudy the implementation (assembler code), you will see that the both functions are implemented withthe same instruction set.Now lets answer the question what for we should replace it++ with ++it then. Lets measure the speedof functions in the Debug version:iterator++. Total time : 83.2849++iterator. Total time : 27.1557There is practical sense in writing the code so that it slows down only 30 times and not 90 times.
  4. 4. Of course, the speed of Debug versions is not so crucial for many programmers. But if a program doessomething for a long time, sure such a large slow-down might be crucial, for instance, from theviewpoint of unit-tests. So, it is reasonable to optimize the speed of the Debug version.I carried out one more experiment to find out what I would get using the good old size_t for indexing. Iknow it doesnt relate to the topic we are discussing and I understand that we cannot compare iteratorswith indexes and that the former are higher-level entities. But still I wrote and measured the speed ofthe following functions just out of curiosity:1) Classic index of the size_t type. i++.for (size_t i = 0; i != arr.size(); i++) sum += arr[i];2) Classic index of the size_t type. ++i.for (size_t i = 0; i != arr.size(); ++i) sum += arr[i];The speed in the Release version:iterator++. Total time : 0.18923++iterator. Total time : 0.18913The speed in the Debug version:iterator++. Total time : 2.1519++iterator. Total time : 2.1493As we had expected, the speeds of i++ and ++i coincided.Note. Code with size_t works faster in comparison to iterators due to absence of array overrun check.We can make the loop with iterators as fast in the Release version by adding the line "#define_SECURE_SCL 0".To make it easier for you to evaluate the results of speed measurements, I present them in the table(Figure 1). I have converted the results taking the running time of Release version with iterator++ for aunit. I also rounded them off a bit to make them clearer.
  5. 5. Figure 1. The running time of sum calculation algorithms.Each one of you can draw your own conclusions. They depend upon tasks you are solving. Personally Icame to the following conclusions: 1. I made sure that it is reasonable to perform such micro-optimization. We should implement the search of postfix increment iterators in PVS-Studio when their previous states are not used. Some programmers will find this functionality useful. All the rest can disable it in the settings if they dont need it. 2. I will always write ++it. I did so before but I did it "just in case". Now I can see how useful it is because I regularly launch debug versions. In general, of course, ++it has a very slight influence on the running time. But if I dont make such small optimizations in different places of the code, it will be too late and the profiler wont help me. Bottlenecks will be spread throughout the code. 3. I notice that the PVS-Studio analyzer is spending more and more time inside various functions of std::vector, std::set, std::string classes and the like. This time is growing more and more because new diagnostic rules appear - and it is quite convenient for us to write them using STL. So, I think - hasnt that frightful time come when the program acquires its own specialized string classes, array classes and so on. Well, it is just my cares... Dont listen to me! I tell people seditious things... Sh!..P. S.Someone will say now that untimely optimization is evil [2]; when you need optimization, you take theprofiler and search for bottlenecks. I know this. And I got rid of certain bottlenecks long ago. But whenIm waiting for the tests to finish for 4 hours, I start thinking that it is a very good idea to gain at least20% of speed. Such optimization is comprised of iterators, structure sizes, avoiding using STL or Boost insome fragments and so on. I believe that some developers agree with me.References 1. Meyers, Scott. More Effective C++: 35 New Ways to Improve Your Programs and Designs. Addison-Wesley, Reading, Mass., 1996. ISBN-10: 020163371X. ISBN-13: 9780201633719. 2. Randall Hyde. The Fallacy of Premature Optimization.