Std :: string performance for handling short strings

Bjarne Stroustrup and other experts have stated that C ++ is faster than C for handling short strings in Bjarne Stroustrup's article and my previous question

But in my test it C++

was about 110% slower than C

.

g ++ version 4.4.6 (works on CentOS 6.3). Is it because g ++ 4.4.6 has less C ++ 11 features like Rvalue Reference (move semantics)

?

Test result

output $ time a.out input_file

minus call function execution timecompose_X()

  • cpp version: 0.192 sec
  • Version C: 0.091 sec

source

compiled with -O2

Edit

compose_cpp()

and compose_p()

taken from Bjarne's article. He said compose_cpp()

more than compose_p()

. I want to verify this fact with real tests.

If I tested incorrectly, how can I improve the test?

#include <iostream>
#include <fstream>

#include <cstdlib>
#include <cstring>

std::string compose_cpp(const std::string& name, const std::string& domain)
{
    return name + '@' + domain;
}

char* compose_c(const char* name, const char* domain)
{
    char* res = (char*) malloc(strlen(name)+strlen(domain)+2);
    char* p = strcpy(res,name);

    p += strlen(name);
    *p = '@';
    strcpy(p+1,domain);

    return res;
}

int main(int argc, char* argv[])
{
    std::ifstream ifs;
    ifs.open(argv[1]);

    std::string email, domain;

    while (ifs.good())
    {
        ifs >> email;
        ifs >> domain;

        // std::string composed = compose_cpp(email, domain);

        char* composed = compose_c(email.c_str(), domain.c_str());
        free(composed);
    }

    ifs.close();
}

      

input file

input file 1 million long. each line is less than 20 bytes, randomly generated.

$ head -n 10 input.txt.1m
9742720 1981857.com
22504 4127435.com
342760 69167.com
53075 26710.com
3837481 1851920.com
98441 278536.com
4503887 9588108.com
193947 90885.com
42603 8166125.com
3587671 297296.com

      

0


source to share


2 answers


I'm just going to guess here because I don't have a data file to test. I think your results may not meet Stroustrup's expectations due to what he says here:

Yes, the C ++ version, because it doesn't need to count the argument characters and doesn't use free storage (dynamic memory) for short argument strings.



However, my understanding is that it libstdc++

uses dynamic memory for all strings (except zero length strings). See this recent SO answer to the small object size question std::string

at libstdc++

: fooobar.com/questions/15034 / ...

You might have better results with an implementation that uses short string optimizations (e.g. MSVC - I'm not sure if clang uses libc ++ or not).

+2


source


I've allowed you to expand your test program a bit. Specifically, I added code to make it generate its data internally and not depend on an external file, added synchronization code to isolate the string being processed, and if it did both C ++ and C versions of string manipulation in the same volume the same launch, so it immediately gave results that I could compare. This gave me the following code:

#include <iostream>
#include <fstream>
#include <vector>
#include <string>

#include <cstdlib>
#include <cstring>

#include <ctime>

char* compose_c(const char* name, const char* domain)
{
    char* res = (char*) malloc(strlen(name)+strlen(domain)+2);
    char* p = strcpy(res,name);

    p += strlen(name);
    *p = '@';
    strcpy(p+1,domain);

    return res;
}

std::string rand_string(int size){
    std::string ret;

    for (int i = 0; i < size; i++)
        ret.push_back(rand() % 10 + '0');
    return ret;
}

struct address {
    std::string email, domain;

    address() : email(rand_string(5)), domain(rand_string(4) + ".com") { }
};

struct composed {
    std::string addr;
    composed(address const &a) : addr(a.email + "@" + a.domain) {}
};

void report(clock_t d, std::string const &label){
    std::cout << double(d) / CLOCKS_PER_SEC << " seconds for " << label << "\n";
}

int main(int argc, char **argv) {
    static const int NUM = 1024 * 1024;

    std::vector<address> addresses(NUM);

    clock_t start = clock();
    {
        std::vector<composed> c{ addresses.begin(), addresses.end() };
    }
    report(clock() - start, "C++");

    std::vector<char *> c_results(addresses.size());

    clock_t start_c = clock();
    for (int i = 0; i < addresses.size(); i++)
        c_results[i] = compose_c(addresses[i].email.c_str(), addresses[i].domain.c_str());
    for (char *c : c_results)
        free(c);
    report(clock() - start_c, "C");
}

      

Then I compiled it with VC ++ 2013, x64 using flags: -O2b2 -GL -EHsc

. When I ran this, I got the result:

0.071 seconds for C++
0.12 seconds for C

      

While there are some run-to-run options, these are pretty representative results - C code takes almost (but not quite) twice as much as C ++ code.

Note that this is despite the fact that I did give the C version a slightly unfair advantage. The time for C ++ code includes not only the execution time of string manipulations, but also the time to create and destroy the vector to store the results. For C code, I prepare the vector ahead of time and then only generate code to create and destroy the strings themselves.

To keep this result from being accidental, I also tried changing some of the explanatory variables. For example, increasing the number of address lines that we compose by a factor of 10 increased the total time, but had little effect on the ratio:

0.714 seconds for C++
1.206 seconds for C

      

Likewise, changing the order to run the C code first and the C ++ code that ran second had no discernible effect.



Suppose I have to add: it's true that since it means this code doesn't actually use the function compose_cpp

as the original did, instead it needs to include the functionality in the constructor for composed

. For completeness, I wrote a version using compose_cpp

, for example:

std::vector<std::string> composed;
composed.reserve(NUM);

clock_t start = clock();

for (auto const &a : addresses)
    composed.push_back(compose_cpp(a.email, a.domain));

      

This actually improves the timing a bit, but my guess is that it is mainly due to the time travel to create the vector itself from the time code and doesn't really matter to take care of it:

0.631 seconds for C++
1.21 seconds for C

      

These results are highly dependent on the standard implementation of the library, in particular because it std::string

implements short string optimizations. Running the same code on the same hardware, but using an implementation that this optimization lacks (nuwen MinGW distribution gcc 4.9.1, in my case) gives completely different results:

2.689 seconds for C++
1.131 seconds for C

      

In this case, the C code is slightly faster than the VC ++ code, but the C ++ code slowed down by about 4 times. I've tried several different compiler flags (-O2 versus -O3, etc.), but they only had minimal effect - for this test, the lack of short string optimization clearly dominates the other factors.

Bottom line: I think this confirms that C ++ code can be significantly faster than C code, but achieving that speed largely depends on the quality of the implementation. If the implementation does not provide short string optimizations, the C ++ code may be 2x slower rather than 2x faster than the C version.

+2


source







All Articles