views:

2476

answers:

11

In terms of performance, what would work faster? Is there a difference? Is it platform dependent?

//1. Using vector<string>::iterator:
vector<string> vs = GetVector();

for(vector<string>::iterator it = vs.begin(); it != vs.end(); ++it)
{
   *it = "Am I faster?";
}

//2. Using size_t index:
for(size_t i = 0; i < vs.size(); ++i)
{
   //One option:
   vs.at(i) = "Am I faster?";
   //Another option:
   vs[i] = "Am I faster?";
}
+5  A: 

Using an iterator results in incrementing a pointer (for incrementing) and for dereferencing into dereferencing a pointer.
With an index, incrementing should be equally fast, but looking up an element involves an addition (data pointer+index) and dereferencing that pointer, but the difference should be marginal.

Benchmark results for 500M iterations, vector size 10, with gcc 4.3.3 (-O3), linux 2.6.29.1 x86_64:
at(): 9158
operator[]: 4269
iterator: 3914ms

YMMV, but if using an index makes the code more readable/understandable, you should do it.

tstenner
Which OS and compiler were the profiled results from? Which implementation of STL were they using? Were the results made with or without optimizations turned on? Be careful, all of this may change the results. To be sure you should profile your own code in your own environment.
Brian R. Bondy
j_random_hacker
-1 Agree with j_random_hacker - if you read the thread all the way through, there's some interesting stuff about the pitfalls of profiling, and also some more reliable results.
James Hopkin
-1, indeed. Quoting numbers without understanding them seems to be a trap that got both tstennner and the bencmarker.
MSalters
+2 now that you've updated with more sensible measuring criteria :)
j_random_hacker
+3  A: 

I would guess the first variant is faster.

But it's implementation dependent. To be sure you should profile your own code.

Why profile your own code?

Because these factors will all vary the results:

  • Which OS
  • Which compiler
  • Which implementation of STL was being used
  • Were optimizations turned on?
  • ... (other factors)
Brian R. Bondy
+1  A: 

The first one will be faster in debug mode because index access creates iterators behind the scene, but in release mode where everything should be inlined, the difference should be negligible or null

Zorglub
+1  A: 

I think the only answer could be a test on your platform. Generally the only thing which is standardized in the STL is the type of iterators a collection offers and the complexity of algorithms.

I would say that there is no (not much of a difference) between those two versions- the only difference I could think of would be tjat the code has to iterate through the whole collection when it has to compute the length of an array (I'm not sure if the length is stored in a variable inside the vector, then the overhead wouldn't matter)

Accessing the elements with "at" should take a little bit longer than directly accessing it with [] because it checks if you are in the bounds of the vector and throws an exception if you are out of bounds (it seems [] is normally just using pointer arithmetic - so it should be faster)

bernhardrusch
+3  A: 

Since you're looking at efficiency, you should realise that the following variations are potentially more efficient:

//1. Using vector<string>::iterator:

vector<string> vs = GetVector();
for(vector<string>::iterator it = vs.begin(), end = vs.end(); it != end; ++it)
{
   //...
}

//2. Using size_t index:

vector<string> vs = GetVector();
for(size_t i = 0, size = vs.size(); i != size; ++i)
{
   //...
}

since the end/size function is only called once rather than every time through the loop. It's likely that the compiler will inline these functions anyway, but this way makes sure.

James Hopkin
The question isn't about how to write efficient code, it is about iterators vs. indexes, but thanks for the input
Gal Goldman
+5  A: 

Why not write a test and find out?

Edit: My bad - I thought I was timing the optimised version but wasn't. On my machine, compiled with g++ -O2, the iterator version is slightly slower than the operator[] version, but probably not significantly so.

#include <vector>
#include <iostream>
#include <ctime>
using namespace std;;

int main() {
    const int BIG = 20000000;
    vector <int> v;
    for ( int i = 0; i < BIG; i++ ) {
     v.push_back( i );
    }

    int now = time(0);
    cout << "start" << endl;
    int n = 0;
    for(vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
     n += *it;
    }

    cout << time(0) - now << endl;
    now = time(0);
    for(size_t i = 0; i < v.size(); ++i) {
     n += v[i];
    }
    cout << time(0) - now << endl;

    return n != 0;
}
anon
Did you test with full optimisation and try it with both the iterator version first and with the array version first? There may be a slight difference in performance but 2x? Not a chance.
James Hopkin
You'll get better performance measurements by using clock() rather than time(), or use whatever high-resolution timer your OS kernel provides.
Kristopher Johnson
I'm not really all thet interested. Higher resolution is a bit meaningless when you consider all the other activities that can be going on in a modern OS while your code runs. But feel free to edit my answer to incorportate your suggestions.
anon
in my tests (using "time" shell builtin and all cout's disabled and one test commented out each time) both versions are equally fast (changed the code so it allocates in the constructor, each element has value "2"). actually the time changes in each test with around 10ms, which i suspect is because of the non-determinism of memory allocation. and sometimes the one, and sometimes the other test is 10ms faster than the other.
Johannes Schaub - litb
@litb - yes, I suspect the slight differences on my machine may be due to its lack of memory. I didn't mean to imply the difference was significant.
anon
on x86 and similar platforms, *(a) requires the same cycles as executes *(a+b) at least for selected registers a and b. There are minor differences - e.g. instruction length and, sometimes, pairing, but generally they should run in the same.
peterchen
+5  A: 

If you don't need indexing, don't use it. The iterator concept is there for your best. Iterators are very easy to optimize, while direct access needs some extra knowledge.

Indexing is meant for direct access. The brackets and the at method do this. at will, unlike [], check for out of bounds indexing, so it will be slower.

The credo is: don't ask for what you don't need. Then the compiler won't charge you for what you don't use.

xtofl
A: 

If you are using VisualStudio 2005 or 2008, to get the best performance out of the vector you'll need to define _SECURE_SCL=0

By default _SECURE_SCL is on which makes iterating over a contain significantly slower. That said leave it on in debug builds, it will make tracking down any errors much easier. One word of caution, since the macro changes the size of iterators and containers, you'll have to be consistent across all compilation units that share a stl container.

Stephen Nutt
+3  A: 

As everyone else here is saying, do benchmarks.

Having said that, I would argue that the iterator is faster since at() does range checking as well, i.e. it throws an out_of_range exception if the index is out of bounds. That check itself propbably incurrs some overhead.

Mats Fredriksson
A: 

The difference should be negligible. std::vector guarantees that its elements are laid out consecutively in memory. Therefore, most stl implementations implement iterators into std::vector as a plain pointer. With this is mind, the only difference between the two versions should be that the first one increments a pointer, and in the second increments an index which is then added to a pointer. So my guess would be the second one is maybe one extremly fast (in terms of cycles) machine instruction more.

Try and check the machine code your compiler produces.

In general, however, the advice would be to profile if it really matters. Thinking about this kind of question prematurely usually does not give you too much back. Usually, your code's hotspots will be elsewhere where you might not suspect it at first sight.

Tobias
A: 

You can use this test code and compare results! Dio it!

#include <vector> 
#include <iostream> 
#include <ctime> 
using namespace std;; 


struct AAA{
    int n;
    string str;
};
int main() { 
    const int BIG = 5000000; 
    vector <AAA> v; 
    for ( int i = 0; i < BIG; i++ ) { 
        AAA a = {i, "aaa"};
        v.push_back( a ); 
    } 

    clock_t now;
    cout << "start" << endl; 
    int n = 0; 
    now = clock(); 
    for(vector<AAA>::iterator it = v.begin(); it != v.end(); ++it) { 
        n += it->n; 
    } 
   cout << clock() - now << endl; 

    n = 0;
    now = clock(); 
    for(size_t i = 0; i < v.size(); ++i) { 
        n += v[i].n; 
    } 
    cout << clock() - now << endl; 

    getchar();
    return n != 0; 
} 
Mostaaf
Uhm … that’s not really all that different form Neil’s code. Why bother posting it?
Konrad Rudolph