c++ - C 风格字符串与库字符串性能

标签 c++

问题

C 风格的字符串操作平均比库 string 类操作慢 5 倍,这是真的吗,正如 C++ Primer,第 4 版让我相信的那样?

为什么要问?

因为当我实际进行性能测试时,结果表明对于特定示例(书中使用的示例),C 风格的字符串要快大约 50%。


设置

我正在阅读 C++ Primer, 4th Edition ,其中(第 138 页)列出了以下代码:

//  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

现在请记住,我知道第 2 行的 strlen(pc +1),第一个 for使用 size_t 但没有下标数组,所以它也可能是 int,但这正是它在书中的写法。

当我测试这段代码时(使用 strlen(pc) + 1,我认为这是有意的),我的结果是第一个 block 的执行速度大约 50%比第二个 block ,这导致得出结论,对于这个特定示例,C 风格的字符串比库字符串类更快

但是,我敢打赌我遗漏了一些东西(可能很明显),因为书中(第 139 页)中写的与上述代码相关的内容:

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

那是哪一个呢?我应该使用更长的字符串文字吗?也许是因为他们使用的是 GNU C 编译器而我使用的是 Microsoft 的?是因为我的电脑速度更快吗?

或者这本书在这一点上错了?

编辑

Microsoft (R) 32 位 C/C++ 优化编译器版本 16.00.40219.01 for 80x86

最佳答案

您得出的结论是,对于您的编译器和机器,此示例使用 C 风格的字符串速度更快,这几乎可以肯定是因为——必须假设——你

  • 忘记打开优化,
  • 忘记使编译器的字符串长度“未知”(这很棘手)以防止它优化 strlen 调用,并且
  • 忘记并关闭安全范围检查(如果适用),这会降低 std::string 的速度。

这是我测试过的代码:

#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;
}

典型结果:

[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]
> _

也就是说,C++ 字符串通常不是最快的字符串实现。

通常,不可变字符串(引用计数)以很大的优势击败 C++ 字符串,而且,当我了解到这一点时,令我惊讶的是,当它使用适当、快速的自定义时,简单地复制字符串数据的字符串实现仍然更快分配器。但是,不要问我如何实现后者。我只在另一个论坛上看到了代码和测试结果,这是我在与 STL 的讨论中指出不可变字符串的普遍优势并且存在一些分歧后有人慷慨地提供的。 ;-)

关于c++ - C 风格字符串与库字符串性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11165237/

相关文章:

c++ - Boost::thread 的可中断线程实现

c++ - 继承 : Selecting which base class methods to inherit

c++ - 在指针上使用重载运算符

c++ - 如何为字符数组制作模板函数特化?

c++ - 使用 std::bitset 实现二进制计数器

c++ - defines/macros/structs 和 consts/funcs/classes 在使用方面有什么区别? (C++)

c++ - C++ 如何通过调用 object.method() 在 object1 的上下文中调用类 method()

c++ - 当整数类型转换为浮点类型时,C++ 中会发生什么,反之亦然?

C++ 带文件的输入/输出

c++ - 在 txt c++ 中读取行