47

Say, I have a

std::vector<SomeClass *> v;

in my code and I need to access its elements very often in the program, looping them forward and backward .

Which is the fastest access type between those two ?

Iterator access:

std::vector<SomeClass *> v;
std::vector<SomeClass *>::iterator i;
std::vector<SomeClass *>::reverse_iterator j;

// i loops forward, j loops backward
for( i = v.begin(), j = v.rbegin(); i != v.end() && j != v.rend(); i++, j++ ){
    // some operations on v items
}

Subscript access (by index)

std::vector<SomeClass *> v;
unsigned int i, j, size = v.size();

// i loops forward, j loops backward
for( i = 0, j = size - 1; i < size && j >= 0; i++, j-- ){
    // some operations on v items
}

And, does const_iterator offer a faster way to access vector elements in case I do not have to modify them?

KiaMorot
  • 1,668
  • 11
  • 22
Simone Margaritelli
  • 4,584
  • 10
  • 45
  • 70
  • 22
    What did the profiler results show you? – Michael Kristofik Mar 26 '10 at 15:09
  • 9
    If i had time and will to profile the code i wouldn't asked here. I'm just wondering if the stl iterator implementations have some sort of access optimization . – Simone Margaritelli Mar 26 '10 at 15:11
  • Consider using `boost::ptr_vector` if the vector owns the objects. Otherwise use `boost::reference_wrapper`. – Björn Pollex Mar 26 '10 at 15:11
  • 1
    @Space_C0wb0y Is 'boost::ptr_vector' (in my case) faster than std::vector ? – Simone Margaritelli Mar 26 '10 at 15:14
  • Asked before: http://stackoverflow.com/questions/776624/whats-faster-iterating-an-stl-vector-with-vectoriterator-or-with-at – neuroguy123 Mar 26 '10 at 15:16
  • @Simone: It's safer and easier. You don't have to worry about looping through the vector, regardless of how it's being destructed. (Exceptions, for example.) – GManNickG Mar 26 '10 at 16:04
  • If speed is really important, you shouldn't store a pointer to SomeClass, but SomeClass itself. This should boost your performance a lot because you'll eliminate all the cache misses. Other than that, in C++11/14, `for(auto&& e : v)` would most likely be the fastest because the compiler can optimise best. – Ela782 Jan 07 '15 at 20:23
  • Possible duplicate of [What's faster, iterating an STL vector with vector::iterator or with at()?](http://stackoverflow.com/questions/776624/whats-faster-iterating-an-stl-vector-with-vectoriterator-or-with-at) – underscore_d May 11 '17 at 18:20
  • @Ela782 I really doubt the range-`for` syntax makes any difference to the generated code, since it is defined precisely as equivalent to a manually written loop using iterators (albeit done right, with a cached `end` and preincrement). – underscore_d May 11 '17 at 18:21
  • @underscore_d You're correct, the compiler generates the iterator code for the range-for loop. – Ela782 May 11 '17 at 19:10
  • I really wish SO has an option to request for removal of answers. I have the same question. The code I am working with is high performance server side where squeezing a couple % can mean $. It would save me a lot of time if someone's done this benchmark. There are 11 answers. Almost all of them are utterly useless noise that I have to wade through. – thang Jun 29 '19 at 04:16

10 Answers10

30

The performance difference is likely negligable or none (the compiler might optimise them to be identical); you should worry about other things, like whether your program is correct (a slow but correct program is better than a fast and incorrect program). There are other advantages to using iterators though, such as being able to change the underlying container to one with no operator[] without modifying your loops. See this question for more.

const_iterators will most likely have none, or negligable, performance difference compared to ordinary iterators. They are designed to improve the correctness of your program by preventing modifying things that shouldn't be modified, not for performance. The same goes for the const keyword in general.

In short, optimisation should not be a concern of yours until two things have happened: 1) you have noticed it runs too slowly and 2) you have profiled the bottlenecks. For 1), if it ran ten times slower than it could, but is only ever run once and takes 0.1ms, who cares? For 2), make sure it's definitely the bottleneck, otherwise optimising it will have nearly no measurable effect on performance!

Community
  • 1
  • 1
AshleysBrain
  • 22,335
  • 15
  • 88
  • 124
  • 7
    I disagree. If using indices instead of iterators is going to give you a performance boost, just use integer indices. You're not losing anything by using indices, and in my opinion it actually looks cleaner (`for( vector::iterator iter = list.begin() ;` vs `for( int i = 0 ;` ) – bobobobo May 30 '12 at 20:23
  • 1
    @bobobobo - but you also lose the ability to swap container easily, like I mentioned - you can't use a std::list with an integer index. – AshleysBrain May 31 '12 at 12:42
  • @AshleysBrain hints at a good point; _the code you write is not the code that is run_. Often times the compiler can do loop optimizations. So the question of iterator vs index probably does not matter since both will generate very similar assembly code anyways. – catalyst294 Jul 23 '14 at 18:37
  • i did a simple loop profiling and found iterator is twice slower in my case and it adds up too fast with overall very slow performance. for parsing i do not recommend using iterators however safe they may look – duckduckgo Jun 17 '15 at 05:05
  • 22
    It seems inappropriate to respond to a question like this with "you shouldn't care" without any knowledge of the circumstances. – Hunter Apr 28 '17 at 16:05
  • 3
    agree with Hunter's sentiment. Doesn't answer the question, and instead gave a snooty "here's how you should do it". – thang Jun 29 '19 at 04:12
  • 3
    I also agree with Hunters comment. This is a largely inappropriate way to offer help and honestly just looks like discouragement from trying to solve a problem. – probsJustin Dec 04 '19 at 20:04
27

A simple loop-based benchmark has been fulfilled. I used VS 2010 SP1 (release configuration).

  1. Use iterators (*it = *it + 1;)
  2. Use indices (vs[i] = vs[i] + 1;)

In several billions of iterations the second approach turned out to be a bit faster, by 1%. The result (indices are slightly faster than iterators) is reproducible but the difference, as I said, is very small.

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
borx
  • 411
  • 4
  • 7
8

I had a test yesterday, use [] vs iterator, the code is create a vector with some elements and remove some elements from the vector. This is the code uses operator [] to access elements

  TimeSpent([](){
    std::vector<int> vt = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
    for (int i = int(vt.size()) - 1; i >= 0; i--)
    {
      if (vt[i] % 2 == 0)
      {
        //cout << "removing " << vt[i] << endl;
        vt.erase(vt.begin() + i);
      }
    }
  });

The following code is about access vector elements by using iterator

  TimeSpent([](){
    std::vector<int> vt = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
    for (std::vector<int>::iterator num = vt.begin(); num != vt.end();)
    {
      if (*num % 2 == 0)
      {
        num = vt.erase(num);
      }
      else
      {
        ++num;
      }
    }
  });

Tested by calling them by this function separately

void TimeSpent(std::function<void()> func)
{
  const int ONE_MIL = 10000;
  long times = ONE_MIL;
  std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
  while (times > 0)
  {
    func();
    --times;
  }
  std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
  cout << "time elapsed : " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << endl;
}


Tested environment is visual studio 2013 pro. version 4.5.51650
The results are :
operator[] : 192
iterator : 212
Summary: when we access the vector container, operator [] is faster than iterator.

r0n9
  • 2,505
  • 1
  • 29
  • 43
2

I believe that vector iterators are implemented as pointers internally (in a good STL implementation), so in general there should be negligible performance difference between the two idioms. But if you want to know how these perform on your platform, why don't you measure it with a little test program? I don't think it would take more than 5 minutes to measure execution time of e.g. 1 million iterations with both variants...

Péter Török
  • 114,404
  • 31
  • 268
  • 329
1

With optimization (-O2) the timings should improve (should be nearly identical).

Jay
  • 11
  • 1
1

As always, it depends. Normally I wouldn't think you'd see any kind of difference, but only you can determine that by profiling your code. Some compilers implement vector iterators as raw pointers, and some don't. Also, in debug builds, some compilers may be using a checked iterator, which may be slower. But in production mode it may not be any different. Profile it and see.

Brian Neal
  • 31,821
  • 7
  • 55
  • 59
0

I was confused about something similar and wrote a program to test the performance : https://github.com/rajatkhanduja/Benchmarks/blob/master/C%2B%2B/vectorVsArray.cpp

Here's the relevant observations for reading/writing to vector<int> of size 1m using g++ (without any optimization flags), on Linux-i686 (64-bit machine) with 7.7 GB RAM:-

Time taken to write to vector using indices. : 11.3909 ms

Time taken to read from vector using indices, sequentially. : 4.09106 ms

Time taken to read from vector using indices, randomly. : 39 ms

Time taken to write to vector using iterators (sequentially). : 24.9949 ms

Time taken to read from vector using iterators (sequentially). : 18.8049 ms

rajatkhanduja
  • 974
  • 1
  • 9
  • 28
  • 6
    What are you compiler flags? `for (int i = 0; i < ARR_SIZE; i++) { tmp = v[i]; }` can easily be optimized to a single statement by any intelligent compiler. In fact, most compilers will notice that **you don't even read the variable anywhere**. The fact that that code still takes 4 ms suggests that you may be compiling with optimizations completely disabled, which would make your benchmarks very misleading. – Ponkadoodle Dec 28 '14 at 10:03
0

In terms of speed, I think might be almost same. Better, you can profile and check anyway.

At least you can reduce the number of variables used :)

for( i = 0; i < size ; i++){
    // some operations on v items
    v[i];
    v[size-i+1];
}

About const_iterator: Pls refer my Q: Are const_iterators faster ?

Community
  • 1
  • 1
aJ.
  • 34,624
  • 22
  • 86
  • 128
  • are you sure that "size - i + 1" for every loop is faster than just a "j--" or better a "--j" ? i think no, so i prefer to have more variables and less clock cicles :P – Simone Margaritelli Mar 26 '10 at 15:13
  • I guess these are micro optimizations and as people say, micro optimizations are evil! – aJ. Mar 26 '10 at 15:16
  • 1
    @Simone: I think that is a premature optimization. A compiler could very well be generating optimal code for aJ's example. Again, if you don't profile you won't know. – Brian Neal Mar 26 '10 at 15:16
0

I'd go for iterators, but what I would optimize is calling end() in the loop and would change preincrement to postincrement. I.e. I'd

std::vector<SomeClass *> v;
std::vector<SomeClass *>::iterator i,ie;
std::vector<SomeClass *>::reverse_iterator j,je;

// i loops forward, j loops backward
for( i=v.begin(),ie=v.end(), j=v.rbegin(),je=v.rend(); i!=ie && j!=je; ++i,++j ){
    // some operations on v items
}

And I don't think it's premature microoptimization, it's just writing better code. Much less evil than calling every attempt to write efficient code premature microoptimization and substituting thinking with profiling.

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
  • And why not removing `j!=je` in the test, as the two conditions are identical? – rafak Apr 26 '10 at 07:35
  • rafak, the conditions aren't identical, even though they should coincide. I just preserved original logic, which is somewhat valid - there's nothing wrong with being on the safe side. I'm unlikely to keep both conditions, though, in my code. – Michael Krelin - hacker Apr 26 '10 at 10:00
-1

You are not only prematurely optimizing, you are micro-optimizing. This is an evil almost as bad as the former (the difference being that very, very, very rarely it is actually necessary to micro-optimize). Put the two together and you've got a recipe for disaster.

If you run a profiler and find this area of code is a bottleneck then you will need to optimize. You don't optimize by trying to reduce your loop from taking 23 clock cycles to 22. You optimize by finding ways to reduce the O() of your algorithm. But until you run a profiler you should be paying more attention to design than any other concern.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125