dimanche 27 décembre 2015

Why is the performance varies different when concatenating many strings?

I'm curious about the performance of concatenation of many strings. After reading Efficient string concatenation in C++, I made some test. But the result is different with different compilers.

Here is my code. (The timer in the code is from here.)

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

template<typename TimeT = std::chrono::milliseconds>
struct measure {
    template<typename F, typename ...Args>
    static typename TimeT::rep execution(F&& func, Args&&... args) {
        auto start = std::chrono::system_clock::now();
        std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
        auto duration = std::chrono::duration_cast< TimeT>
            (std::chrono::system_clock::now() - start);
        return duration.count();
    }
};

std::string strAppend(const std::string &s, int cnt) {
    std::string str;
    for (int i = 0; i < cnt; ++i)
        str.append(s);
    return str;
}

std::string strOp(const std::string &s, int cnt) {
    std::string str;
    for (int i = 0; i < cnt; ++i)
        str += s;
    return str;
}

std::string strStream(const std::string &s, int cnt) {
    std::ostringstream oss;
    for (int i = 0; i < cnt; ++i)
        oss << s;
    return oss.str();
}

std::string strReserveAndOp(const std::string &s, int cnt) {
    std::string str;
    str.reserve(s.size() * cnt);
    for (int i = 0; i < cnt; ++i)
        str += s;
    return str;
}

std::string strReserveAndAppend(const std::string &s, int cnt) {
    std::string str;
    str.reserve(s.size() * cnt);
    for (int i = 0; i < cnt; ++i)
        str.append(s);
    return str;
}

int main() {
    const std::string s("Hello world!");
    const int cnt = 1000000 * 5;
    std::cout << "ostringstream: " << measure<>::execution(strStream, s, cnt) << "ms" << std::endl;
    std::cout << "+= operator: " << measure<>::execution(strOp, s, cnt) << "ms" << std::endl;
    std::cout << "s.Append(): " << measure<>::execution(strAppend, s, cnt) << "ms" << std::endl;
    std::cout << "s.Reserve() & +=: " << measure<>::execution(strReserveAndOp, s, cnt) << "ms" << std::endl;
    std::cout << "s.Reserve() & s.Append(): " << measure<>::execution(strReserveAndAppend, s, cnt) << "ms" << std::endl;
}

Tested on Ideone with GCC 5.1 gives the result:

ostringstream: 602ms
+= operator: 345ms
s.Append(): 336ms
s.Reserve() & +=: 224ms
s.Reserve() & s.Append(): 225ms

In which the ostringstream is the slowest and the reserve() speed up a little bit.

However when running the same code on Visual Studio 2015 community, the result is as follows:

ostringstream: 4413ms
+= operator: 9319ms
s.Append(): 8937ms
s.Reserve() & +=: 8966ms
s.Reserve() & s.Append(): 8815ms

Just pay attention to the relative speed, the ostringstream becomes the fastest and the reserve() seems not speed up.

So why is that happened? Compiler optimization or something else?

Aucun commentaire:

Enregistrer un commentaire