C-Style strings versus library string performance

c++

Solution

Your conclusion that C style strings are faster with this example with your compiler & machine, is almost certainly because – one must presume – you

- forgot to turn on optimization,

- forgot to make the string length "unknown" to the compiler (this is tricky) so as to prevent it from optmizing away `strlen` calls, and

- forgot and turn off safety range checking (if applicable) which would slow down `std::string`.

Here's the code I tested with:

#include <assert.h>
#include <iostream>
#include <time.h>
#include <string>
#include <string.h>
using namespace std;

extern void doNothing( char const* );

class StopWatch
{
private:
    clock_t     start_;
    clock_t     end_;
    bool        isRunning_;
public:
    void start()
    {
        assert( !isRunning_ );
        start_ = clock();
        end_ = 0;
        isRunning_ = true;
    }

    void stop()
    {
        if( isRunning_ )
        {
            end_ = clock();
            isRunning_ = false;
        }
    }

    double seconds() const
    {
        return double( end_ - start_ )/CLOCKS_PER_SEC;
    }

    StopWatch(): start_(), end_(), isRunning_() {}
};

inline void testCStr( int const argc, char const* const argv0 )
{
    //  C-style character string implementation
    //const char *pc = "a very long literal string";
    const char *pc = (argc == 10000? argv0 : "a very long literal string");
    //const size_t  len = strlen(pc +1);    //  space to allocate
    const size_t  len = strlen(pc)+1;    //  space to allocate

    //  performance test on string allocation and copy
    for (size_t ix = 0; ix != 1000000; ++ix) {
        char *pc2 = new char[len + 1];  //  allocate the space
        strcpy(pc2, pc);                //  do the copy
        if (strcmp(pc2, pc))            //  use the new string
            //;   //  do nothing
            doNothing( pc2 );
        delete [] pc2;                  //  free the memory
    }
}

inline void testCppStr( int const argc, char const* const argv0 )
{
    //  string implementation
    //string str("a very long literal string");
    string str( argc == 10000? argv0 : "a very long literal string" );

    //  performance test on string allocation and copy
    for(int ix = 0; ix != 1000000; ++ix) {
        string str2 = str;  //  do the copy, automatically allocated
        if (str != str2)    //  use the new string
            //;   //  do nothing
            doNothing( &str2[0] );
    }    //  str2 is automatically freed
}

int main( int argc, char* argv[] )
{
    StopWatch   timer;

    timer.start();  testCStr( argc, argv[0] );  timer.stop();
    cout << "C strings: " << timer.seconds() << " seconds." << endl;

    timer.start();  testCppStr( argc, argv[0] );  timer.stop();
    cout << "C++ strings: " << timer.seconds() << " seconds." << endl;
}

Typical result:

[d:\dev\test]
> g++ foo.cpp doNothing.cpp -O2

[d:\dev\test]
> a
C strings: 0.417 seconds.
C++ strings: 0.084 seconds.

[d:\dev\test]
> a
C strings: 0.398 seconds.
C++ strings: 0.082 seconds.

[d:\dev\test]
> a
C strings: 0.4 seconds.
C++ strings: 0.083 seconds.

[d:\dev\test]
> _

The said, C++ strings are not generally the fastest possible implementation of strings.

Generally, immutable strings (reference counted) beat C++ strings by a good margin, and, surprising to me when I learned that, a string implementation that simply copies the string data is faster still, when it uses an appropriate, fast custom allocator. However, don't ask me how to implement the latter. I only saw the code and test results in another forum, which someone graciously provided after I'd pointed out the general superiority of immutable strings in a discussion with STL and there was some disagreement. ;-)

Problem

Question Is it true that C-style strings operations, on average, execute 5 times slower than library `string` class operations, as C++ Primer, 4th Edition would have me believe? Why ask? Because when I actually performance test, it turns out that C-style strings are about 50% faster for a particular example (one used in the book). Setup I am reading C++ Primer, 4th Edition, which (on page 138) lists this code: ``` // C-style character string implementation const char *pc = "a very long literal string"; const size_t len = strlen(pc +1); // space to allocate // performance test on string allocation and copy for (size_t ix = 0; ix != 1000000; ++ix) { char *pc2 = new char[len + 1]; // allocate the space strcpy(pc2, pc); // do the copy if (strcmp(pc2, pc)) // use the new string ; // do nothing delete [] pc2; // free the memory } // string implementation string str("a very long literal string"); // performance test on string allocation and copy for(int ix = 0; ix != 1000000; ++ix) { string str2 = str; // do the copy, automatically allocated if (str != str2) // use the new string ; // do nothing } // str2 is automatically freed ``` Now bear in mind that I'm aware of that `strlen(pc +1)` on line 2, and that the first `for` uses `size_t` but doesn't subscript the array so it might as well have been `int`, but this is exactly how it is written down in the book. When I test this code (with `strlen(pc) + 1`, which I presume was intended), my results are that the first block executes about 50% faster than the second block, which leads to conclusion that C-style strings are faster than library string class for this particular example. However, I bet I'm missing something (probably obvious), because of what is written in the book (page 139) relating to the code above: As it happens, on average, the string class implementation executes considerably faster than the C-style string functions. The relative average execution times on our more than five-year-old PC are as follows: ``` user 0.47 # string class user 2.55 # C-style character string ``` So which one is it? Should I have used a longer string literal? Maybe it was because they used the GNU C Compiler and I used the Microsoft one? Is it because I have a faster computer? Or is the book just wrong on this one? Edit Microsoft (R) 32-bit C/C++ Optimizing Compiler version 16.00.40219.01 for 80x86

Original source