João Pires
João Pires

Reputation: 1007

Range-based for loop, which one is faster?

If we use the range-based for loop with strings, is there any gain using

for (auto const & c : s)

over

for (auto c : s)

// c => char
// s => std::string

I'm asking this because chars which are just one byte in memory are not that expensive to copy, and this kept me curious last night.

I did some benchmarks on it!

RESULT:

// In Milliseconds

// 1000
// BY COPY: 7
// BY _REF: 5

// 10000
// BY COPY: 51
// BY _REF: 50

// 100000
// BY COPY: 503
// BY _REF: 501

// 1000000
// BY COPY: 5047
// BY _REF: 5101

// 10000000
// BY COPY: 52058
// BY _REF: 56160

CODE:

#include <chrono>
#include <iostream>
#include <string>

using std::cout;
using std::endl;

bool by_copy(std::string const & s);
bool by_const_ref(std::string const & s);

int main() {
    std::string const test {"0000000001"};

    auto start {std::chrono::steady_clock::now()};
    for (unsigned long long i {}; i < 10000000; ++i) {
        bool b {by_copy(test)};
        if (b) {}
    }
    auto end {std::chrono::steady_clock::now()};
    auto duration {std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()};
    cout << "BY COPY: " << duration << '\n';

    start = std::chrono::steady_clock::now();
    for (unsigned long long i {}; i < 10000000; ++i) {
        bool b {by_const_ref(test)};
        if (b) {}
    }
    end = std::chrono::steady_clock::now();
    duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    cout << "BY _REF: " << duration << '\n';

    return 0;
}

bool by_copy(std::string const & s) {
    for (auto c : s) {
        if (c == '1') {
            return true;
        }
    }
    return false;
}

bool by_const_ref(std::string const & s) {
    for (auto const & c : s) {
        if (c == '1') {
            return true;
        }
    }
    return false;
}

UPDATE

Out of curiosity, I also did benchmarks on by index, and it was way much more faster than the range-based for loops, why?

RESULTS

    // 1000
    // BY COPY: 7
    // BY _REF: 5
    // BYINDEX: 4

    // 10000
    // BY COPY: 59
    // BY _REF: 58
    // BYINDEX: 37

    // 100000
    // BY COPY: 526
    // BY _REF: 495
    // BYINDEX: 326

    // 1000000
    // BY COPY: 5751
    // BY _REF: 5038
    // BYINDEX: 3308

    // 10000000
    // BY COPY: 62202
    // BY _REF: 63002
    // BYINDEX: 38744

the by_index function:

bool by_index(std::string const & s) {
    for (size_t i {}; i < s.size(); ++i) {
        if (s[i] == '1') {
            return true;
        }
    }
    return false;
}

Upvotes: 0

Views: 308

Answers (1)

eerorika
eerorika

Reputation: 238351

To clarify, I interpret your question such that you are iterating over the characters of a string - rather than iterating a container of strings.

No, there is no advantage. In fact, there is theoretically potential for the reference being slower due to the implied indirection. However, it is likely that both variations compile to exactly the same machine code because a decent optimizer will be able to avoid the indirections.

Upvotes: 1

Related Questions