How efficient is std::string compared to null-terminated strings?

前端 未结 14 2032
心在旅途
心在旅途 2020-12-28 19:23

I\'ve discovered that std::strings are very slow compared to old-fashioned null-terminated strings, so much slow that they significantly slow down my overall pr

相关标签:
14条回答
  • 2020-12-28 19:56

    When writing C++ code using any utility class (whether STL or your own) instead of eg. good old C null terminated strings, you need to rememeber a few things.

    • If you benchmark without compiler optimisations on (esp. function inlining), classes will lose. They are not built-ins, even stl. They are implemented in terms of method calls.

    • Do not create unnesessary objects.

    • Do not copy objects if possible.

    • Pass objects as references, not copies, if possible,

    • Use more specialised method and functions and higher level algorithms. Eg.:

      std::string a = "String a"
      std::string b = "String b"
      
      // Use
      a.swap(b);
      
      // Instead of
      std::string tmp = a;
      a = b;
      b = tmp;
      

    And a final note. When your C-like C++ code starts to get more complex, you need to implement more advanced data structures like automatically expanding arrays, dictionaries, efficient priority queues. And suddenly you realise that its a lot of work and your classes are not really faster then stl ones. Just more buggy.

    0 讨论(0)
  • 2020-12-28 19:59

    You are most certainly doing something wrong, or at least not comparing "fairly" between STL and your own code. Of course, it's hard to be more specific without code to look at.

    It could be that you're structuring your code using STL in a way that causes more constructors to run, or not re-using allocated objects in a way that matches what you do when you implement the operations yourself, and so on.

    0 讨论(0)
  • 2020-12-28 19:59

    If you have an indication of the eventual size of your vector you can prevent excessive resizes by calling reserve() before filling it up.

    0 讨论(0)
  • 2020-12-28 20:04

    std::string will always be slower than C-strings. C-strings are simply a linear array of memory. You cannot get any more efficient than that, simply as a data structure. The algorithms you use (like strcat() or strcpy()) are generally equivalent to the STL counterparts. The class instantiation and method calls will be, in relative terms, significantly slower than C-string operations (even worse if the implementation uses virtuals). The only way you could get equivalent performance is if the compiler does optimization.

    0 讨论(0)
  • 2020-12-28 20:06

    If used correctly, std::string is as efficient as char*, but with the added protection.

    If you are experiencing performance problems with the STL, it's likely that you are doing something wrong.

    Additionally, STL implementations are not standard across compilers. I know that SGI's STL and STLPort perform generally well.

    That said, and I am being completely serious, you could be a C++ genius and have devised code that is far more sophisticated than the STL. It's not likely , but who knows, you could be the LeBron James of C++.

    0 讨论(0)
  • 2020-12-28 20:07

    A large part of the reason might be the fact that reference-counting is no longer used in modern implementations of STL.

    Here's the story (someone correct me if I'm wrong): in the beginning, STL implementations used reference counting, and were fast but not thread-safe - the implementors expected application programmers to insert their own locking mechanisms at higher levels, to make them thread-safe, because if locking was done at 2 levels then this would slow things down twice as much.

    However, the programmers of the world were too ignorant or lazy to insert locks everywhere. For example, if a worker thread in a multi-threaded program needed to read a std::string commandline parameter, then a lock would be needed even just to read the string, otherwise crashes could ensue. (2 threads increment the reference count simultaneously on different CPU's (+1), but decrement it separately (-2), so the reference count goes down to zero, and the memory is freed.)

    So implementors ditched reference counting and instead had each std::string always own its own copy of the string. More programs worked, but they were all slower.

    So now, even a humble assignment of one std::string to another, (or equivalently, passing a std::string as a parameter to a function), takes about 400 machine code instructions instead of the 2 it takes to assign a char*, a slowdown of 200 times.

    I tested the magnitude of the inefficiency of std::string on one major program, which had an overall slowdown of about 100% compared with null-terminated strings. I also tested raw std::string assignment using the following code, which said that std::string assignment was 100-900 times slower. (I had trouble measuring the speed of char* assignment). I also debugged into the std::string operator=() function - I ended up knee deep in the stack, about 7 layers deep, before hitting the 'memcpy()'.

    I'm not sure there's any solution. Perhaps if you need your program to be fast, use plain old C++, and if you're more concerned about your own productivity, you should use Java.

    #define LIMIT 800000000
    clock_t start;
    std::string foo1 = "Hello there buddy";
    std::string foo2 = "Hello there buddy, yeah you too";
    std::string f;
    
    start = clock();
    for (int i=0; i < LIMIT; i++) {
        stop();
        f    = foo1;
        foo1 = foo2;
        foo2 = f;
    }
    double stl = double(clock() - start) / CLOCKS_PER_SEC;
    
    start = clock();
    for (int i=0; i < LIMIT; i++) {
        stop();
    }
    double emptyLoop = double(clock() - start) / CLOCKS_PER_SEC;
    
    char* goo1 = "Hello there buddy";
    char* goo2 = "Hello there buddy, yeah you too";
    char *g;
    
    start = clock();
    for (int i=0; i < LIMIT; i++) {
        stop();
        g = goo1;
        goo1 = goo2;
        goo2 = g;
    }
    double charLoop = double(clock() - start) / CLOCKS_PER_SEC;
    
    TfcMessage("done", 'i', "Empty loop = %1.3f s\n"
                            "char* loop = %1.3f s\n"
                            "std::string loop = %1.3f s\n\n"
                            "slowdown = %f", 
                            emptyLoop, charLoop, stl, 
                            (stl - emptyLoop) / (charLoop - emptyLoop));
    
    0 讨论(0)
提交回复
热议问题