C++ iterators & loop optimization

前端 未结 11 1156
渐次进展
渐次进展 2021-01-30 04:11

I see a lot of c++ code that looks like this:

for( const_iterator it = list.begin(),
     const_iterator ite = list.end();
     it != ite; ++it)
<
相关标签:
11条回答
  • 2021-01-30 04:27

    I'll just mention for the record that the C++ standard mandates that calling begin() and end() on any container type (be it vector, list, map etc.) must take only constant time. In practice, these calls will almost certainly be inlined to a single pointer comparison if you compile with optimisations turned on.

    Note that this guarantee does not necessarily hold for additional vendor-supplied "containers" that do not actually obey the formal requirements of being a container laid out in the chapter 23 of the standard (e.g. the singly-linked list slist).

    0 讨论(0)
  • 2021-01-30 04:27

    I would choose the option that is most concise and readable. Don't try to second guess the compiler and the optimisations it might perform. Remember that the vast majority of your code will have absolutely no effect on overall performance, so only if this is in a performance critical section of code should you spend the time to profile it and choose an appropriately efficient source representation.

    With specific reference to your example, the first version makes a copy of the end() iterator, invoking whatever code runs for the copy constructor of the iterator object. STL containers generally contain inline end() functions, so the compiler has plenty of opportunity to optimise the second version even if you're not trying to help it out. Which one is best? Measure them.

    0 讨论(0)
  • 2021-01-30 04:29

    Aah, people seem to be making guesses. Open up your code in the debugger & you will see that the calls to begin(), end() etc everything is optimized away. There is no need to use version 1. Tested with Visual C++ compiler fullopt.

    0 讨论(0)
  • 2021-01-30 04:30

    The two versions are not the same though. In the second version it compares the iterator against list.end() every time, and what list.end() evaluates to might change over the course of the loop. Now of course, you cannot modify list through the const_iterator it; but nothing prevents code inside the loop from just calling methods on list directly and mutating it, which could (depending on what kind of data structure list is) change the end iterator. It might therefore be incorrect in some circumstances to store the end iterator beforehand, because that may no longer be the correct end iterator by the time you get to it.

    0 讨论(0)
  • 2021-01-30 04:32
    1. Sample it under stress conditions and see if you're in ** this code very often ***.
      If not, it doesn't matter.

    2. If you are, look at the disassembly, or single-step it.
      That's how you can tell which is faster.

    You gotta be careful of these iterators.
    They may get optimized into nice machine code, but often enough they don't, and become time hogs.

    ** (Where "in" means actually in it, or being called from it.)

    *** (Where "often" means a significant percentage of the time.)

    ADDED: Don't just see how many times per second the code is executed. It could be 1,000 times a second and still be using less than 1% of the time.

    Don't time how long it takes either. It could take a millisecond and still be using less than 1% of the time.

    You could multiply the two, to get a better idea, but that only works if they're not too skewed.

    Sampling the call stack will tell you if it uses a high enough percentage of time to matter.

    0 讨论(0)
  • 2021-01-30 04:35

    Consider this example:

    for (const_iterator it = list.begin(); it != list.end(); ++list)
    {
        if (moonFull())
            it = insert_stuff(list);
        else
            it = erase_stuff(list);
    }
    

    in this case, you NEED to call list.end() in the loop, and the compiler isn't going to optimize that away.

    Other cases where the compiler can prove that end() is always returning the same value, optimization can take place.

    If we are talking about STL containers, than i think any good compiler can optimize away multiple end() calls when multiple end() calls is not needed for the programming logic. However, if you have a custom container and implementation of end() is not in the same translation unit, than optimization will have to happen at link time. I know very little about link time optimization, but i'll bet most linkers will not do such optimization.

    0 讨论(0)
提交回复
热议问题