Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Foreach range iteration over a vector<int> - auto or auto&?

Game engine micro-optimization situation: I'm using C++11 range for loop to iterate over a vector<int>, with the auto keyword.

What is faster:

for(auto value : ints) ...

or

for(auto& value : ints) ...

?

like image 316
Vittorio Romeo Avatar asked May 13 '13 14:05

Vittorio Romeo


2 Answers

Before caring about which is faster, you should care about which is semantically correct. If you do not need to alter the element being iterated, you should choose the first version. Otherwise, you should choose the second version.

Sure, you could object that even if you do not need to alter the content of the vector, there is still the option to use a reference to const:

 for(auto const& value : ints)

And then the question becomes: Which is faster? By reference to const or by value?

Well, again, you should first consider whether the above is semantically correct at all, and that depends on what you are doing inside the for loop:

int i = 0;
for (auto const& x : v) // Is this correct? Depends! (see the loop body)
{
    v[i] = 42 + (++i);
    std::cout << x;
}

This said, for fundamental types I would go with for (auto i : x) whenever this is semantically correct.

I do not expect performance to be worse (rather, I expect it to be better), but as always when it comes to performance, the only meaningful way to back up your assumptions is to measure, measure, and measure.

like image 91
Andy Prowl Avatar answered Nov 15 '22 08:11

Andy Prowl


If you modify value and expect it to modify an actual element in the vector you need auto&. If you don't modify value it likely compiles into the exact same code with auto or auto& (profile it to find out for yourself).

I did some timing using VS2012 with a timer based on QueryPerformanceCounter...

    m::HighResTimer timer;

    std::vector<int> ints(100000000, 17);

    int count = 0;

    timer.Start();
    for(auto& i : ints)
        count += i;
    timer.Stop();

    std::cout   << "Count: " << count << '\n'
                << "auto& time: " << duration_cast<duration<double, std::milli>>(timer.Elapsed()).count() << '\n';

    count = 0;
    timer.Reset();
    timer.Start();
    for(const auto& i : ints)
        count += i;
    timer.Stop();

    std::cout   << "Count: " << count << '\n'
                << "const auto& time: " << duration_cast<duration<double, std::milli>>(timer.Elapsed()).count() << '\n';

    count = 0;
    timer.Reset();
    timer.Start();
    for(auto i : ints)
        count += i;
    timer.Stop();

    std::cout   << "Count: " << count << '\n'
                << "auto time: " << duration_cast<duration<double, std::milli>>(timer.Elapsed()).count() << '\n';

The Results....

Count: 1700000000
auto& time: 77.0204

Count: 1700000000
const auto& time: 77.0648

Count: 1700000000
auto time: 77.5819
Press any key to continue . . .

I wouldn't read into the time differences here. For all practical purposes they are identical, and fluctuate slightly run to run.

like image 25
David Avatar answered Nov 15 '22 07:11

David