与C字符串(`malloc` +`memcpy`)相比,`std :: string`的性能真的很糟糕

时间:2014-02-28 10:25:23

标签: c++ string visual-studio-2012 stl

我偶然发现了std::string的糟糕表现。我预计来自某些外部数据(例如std::string)的新std::string(X.c_str())创建将大致相当于data = malloc(X.size()) + strcpy(data, X.c_str()),并且会有一些小的常量开销。

一些示例性能代码:

#include <string>
#include <string.h>
#include <assert.h>

static const char SampleString[] =
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
    "Hello world. Hello world. Hello world. Hello world. Hello world. "
;

static size_t N = 1000000;

// mostly for avoiding compiler optimization
void readStr(const char* _s) {
    volatile const char* s = _s;
    while(*s) ++s;
}

void cppStringLoop1() {
    for(size_t i = 0; i < N; ++i) {
        std::string tmp(SampleString);
        readStr(&tmp[0]);
    }
}

void cppStringLoop2() {
    for(size_t i = 0; i < N; ++i) {
        std::string tmp(SampleString, SampleString + sizeof(SampleString));
        readStr(&tmp[0]);
    }
}

void cStringLoop() {
    for(size_t i = 0; i < N; ++i) {
        char* tmp = (char*) malloc(sizeof(SampleString));
        memcpy(tmp, SampleString, sizeof(SampleString));
        readStr(tmp);
        free(tmp);
    }
}

int main(int argc, char** argv) {
    assert(argc >= 2);
    if(strcmp(argv[1], "-c") == 0) cStringLoop();
    else if(strcmp(argv[1], "-c++1") == 0) cppStringLoop1();
    else if(strcmp(argv[1], "-c++2") == 0) cppStringLoop2();
    else assert(false);
    return 0;
}

似乎MSVC处于发布模式,我的初步假设是正确的。 (发布模式= MSVC发布运行时lib +优化。)

但是,在调试模式下(MSVC Debug Runtime lib +没有优化),看起来这个假设是错误的。开销不是很小(约为175%)。

也许它也是MSVC 2012 std::string实施。这里有一些数字:

$ time ./TestStringPerf.exe -c

real    0m6.879s
user    0m0.015s
sys     0m0.015s

$ time ./TestStringPerf.exe -c++1

real    0m10.524s
user    0m0.000s
sys     0m0.000s

$ time ./TestStringPerf.exe -c++2

real    0m10.106s
user    0m0.000s
sys     0m0.015s

或许这只是期望的开销。

1 个答案:

答案 0 :(得分:8)

您继续使用sizeof(SampleString)SampleString是一个指针。因此,您的C代码和cppStringLoop2函数仅复制大约4-8个字符。

您需要:

  • 将您对sizeof(SampleString)的使用更改为std::strlen(SampleString);
  • 或将static const char* SampleString更改为static const char SampleString[]并在某些地方使用sizeof(SampleString),在其他地方使用sizeof(SampleString) - 1(即cppStringLoop2功能)。