【问题标题】:How to do fast string concatenation in c++如何在 C++ 中进行快速字符串连接
【发布时间】:2016-09-18 13:16:45
【问题描述】:

我正在使用长 std::string(std::wstring) 处理

原始字符串的长度可以是10到100万,我得到了一些子字符串的偏移量。我需要的是连接原始字符串的多个子字符串和一些新字符串。

除了使用字符串追加

auto str = new std::string();
str.append(original.substr(a,b)).append(newstr1).append(original.substr(c,d)).append.....

有没有更有效的方法,比如处理指针或字符串迭代器?

谢谢。

更新:

我现在收到了一些反馈,除了绳索我可以测试所有其他方法 结果如下:

#include <string>
#include <iostream>
#include <chrono>
#include <ctime>
std::string GetSystemTimeEpoch(){
    using namespace std::chrono;
    auto now = system_clock::now();
    time_point<system_clock> epoch;
    microseconds ms = duration_cast<milliseconds>(now - epoch);
    double epoch_time = (unsigned long long)ms.count() / 1000000.0;
    unsigned long long postfix = (unsigned long long)ms.count() % 1000000;
    std::time_t   time = static_cast<time_t>(epoch_time);
    std::tm tm = *std::localtime(&time);
    char Buf[80];
    std::strftime(Buf, sizeof(Buf), "%Y-%m-%dT%H:%M:%S", &tm);
    std::string finaltime(Buf);
    return finaltime.append(".").append(std::to_string(postfix));
}

#define TESTLENGTH1 1000000000
#define TESTLENGTH2 300000000
int main(){
    std::string Str(TESTLENGTH2, 'c');
    std::cout << GetSystemTimeEpoch() << " Begin of Method 1(replace)"<< std::endl;
    for (size_t i = 0; i < Str.length(); i++){
        Str.replace(i, 1, "d");
    }
    std::cout << GetSystemTimeEpoch() << " Begin of Method 2(append)" << std::endl;
    std::string NewStr1;
    for (size_t i = 0; i < Str.length(); i++){
        NewStr1.append(Str.substr(i, 1));
    }
    std::cout << GetSystemTimeEpoch() << " Begin of Method 3(+=)" << std::endl;
    std::string NewStr2;
    for (size_t i = 0; i < Str.length(); i++){
        NewStr2 += Str.substr(i, 1);
    }
    std::cout << GetSystemTimeEpoch() << " Begin of Method 4(reserve)" << std::endl;
    std::string NewStr3;
    NewStr3.reserve(TESTLENGTH2);
    for (size_t i = 0; i < Str.length(); i++){
        NewStr3 += Str.substr(i, 1);
    }
    std::cout << GetSystemTimeEpoch() << " End" << std::endl;
    return 0;
}

===

2016-05-21T22:38:51.471000 Begin of Method 1(replace)
2016-05-21T22:38:58.972000 Begin of Method 2(append)
2016-05-21T22:39:14.429000 Begin of Method 3(+=)
2016-05-21T22:39:29.944000 Begin of Method 4(reserve)
2016-05-21T22:39:44.892000 End
Press any key to continue . . .

似乎最快的方法不是进行连接而是替换 用于连接。(方法1)

连接方法(2,3,4)好像没有区别。

我没有测试过 sgi ROPE 课程,因为我找不到初学者文档开始 :)。如果有人知道,请留下草图或完成此测试用例。

PS。 TESTLENGTH1 在方法 2 和 3 和 4 中崩溃

PS2。 测试环境,Win7x64;VC++2013;目标Win32,Release。 i5 2GHz,8GB 内存

【问题讨论】:

  • 注意:C++ 不是 Jave 或 C#:不要(!)不必要地使用新的东西。
  • 您是否分析了当前代码以确保您甚至应该关心这一点?
  • Ed Heal:例如 DNA 碱基序列。
  • reserve 大量容量用于结果字符串,稍后用shrink_to_fit 截断内存
  • @DieterLücking 这听起来不错,保留字符串追加速度会更快还是我会做一些“内存复制方法”?

标签: c++ string


【解决方案1】:

来自 SGI 的原始 STL 有一个名为 rope 的数据结构。这存储了一个子序列数组,因此您的新序列的构造将是 O(1)。

请参阅this 答案。您可以从here 下载 SGI STL。

【讨论】:

  • 好吧,O(子序列数);但这可能比字符总数少得多。
  • 似乎不错,但该模块不是官方的,并且几乎没有 googleble 示例,我必须先做一些研究才能决定是否可以采取答案:)
  • 哦,它当然不是 C++ 标准的一部分,但它是一个有用的库,您可以将其添加到您的项目中。
【解决方案2】:

第一种方法是使用Martin Bonner 的方法。 我不太确定,但值得一试。


第二种方法是使用运算符+(或+=)。
它会让你的代码更短(甚至更快)。


而且,您说长度可能是 10 到 100 万。 那么这是一个好消息! 根据 string::max_size,字符串的最大长度几乎是 4.29 亿,所以这不是你应该担心的事情。

【讨论】:

  • += 短代码,但速度似乎相同:),请参阅更新
  • @Matthew Roh 我认为您的意思是 42.9 亿,即 2^32-1,这是 32 位地址空间的大小。在具有超过 4GB 地址空间的 64 位系统上,max_size 高得多,以至于它没有意义,因为它没有考虑可用的物理 RAM。例如,这是我在 linux 系统上得到的:9 223 372 036 854 775 807(即 2^63-1)。当然,我没有足够的 RAM + 交换空间来保存这么多数据。
【解决方案3】:

关于测试:您应该使用分析工具或编写适当的测试用例来测量执行时间:

#include <string>
#include <iostream>
#include <chrono>
#include <random>

using std::chrono::system_clock;

#define TESTLENGTH 100000000

std::string random_string() {
    std::random_device random_device;
    std::mt19937 random_generator(random_device());
    std::uniform_int_distribution<char>distribution;
    std::string result(TESTLENGTH, 0);
    for(auto& c :result)
        c = distribution(random_generator);
    return result;
}

void print_duration(const system_clock::time_point& start, const system_clock::time_point& stop) {
    using namespace std::chrono;
    auto duration = duration_cast<milliseconds>(stop - start);
    std::cout << duration.count() << std::endl;
}

void utilize(const std::string& str)
{
    static volatile char* result = new char[TESTLENGTH];;
    std::copy(str.begin(), str.begin() + std::max(str.size(), std::string::size_type(TESTLENGTH)), result);
}

int main(){
    for(unsigned loop = 0; loop < 4; ++loop) {
        std::cout << "Method 1(replace): "<< std::endl;
        {
            std::string  Str = random_string();
            auto start = system_clock::now();
            std::string NewStr(TESTLENGTH, 0);
            for (size_t i = 0; i < Str.length(); i++){
                NewStr.replace(i, 1, 1, Str[i]);
            }
            auto stop = system_clock::now();
            print_duration(start, stop);
            utilize(NewStr);
        }
        std::cout << "Method 2(append)" << std::endl;;
        {
            std::string  Str = random_string();
            auto start = system_clock::now();
            std::string NewStr;
            for (size_t i = 0; i < Str.length(); i++){
                NewStr.append(1, Str[i]);
            }
            auto stop = system_clock::now();
            print_duration(start, stop);
            utilize(NewStr);
        }
        std::cout << "Method 3(+=)" << std::endl;
        {
            std::string  Str = random_string();
            auto start = system_clock::now();
            std::string NewStr;
            for (size_t i = 0; i < Str.length(); i++){
                NewStr += Str[i];
            }
            auto stop = system_clock::now();
            print_duration(start, stop);
            utilize(NewStr);
        }
        std::cout << "Method 4(reserve)" << std::endl;
        {
            std::string  Str = random_string();
            auto start = system_clock::now();
            std::string NewStr;
            NewStr.reserve(TESTLENGTH);
            for (size_t i = 0; i < Str.length(); i++){
                NewStr += Str[i];
            }
            auto stop = system_clock::now();
            print_duration(start, stop);
            utilize(NewStr);
        }
    }
    return 0;
}

注意事项:

代码不反映原始问题(结果字符串仅附加单个且没有字符序列),但它是问题中显示的代码的改进。

我将replace 方法与其他方法进行了比较。

为了防止不必要的开销,时间测量保持在最低限度(不使用可怕的GetSystemTimeEpoch

为了避免不必要的开销,我删除了std::string::substr

为了防止不必要的编译器优化:

  • 输入是随机的
  • 使用结果(通过将其复制到易失性地址)

为了获得更可靠的结果,多次执行测量(也许应该超过 4 次)。

结果:

g++ 4.8.4 和 g++ -std=c++11 -O3 我的测量结果是:

Method 1(replace): 
1766
Method 2(append)
1292
Method 3(+=)
684
Method 4(reserve)
628
Method 1(replace): 
1766
Method 2(append)
1275
Method 3(+=)
678
Method 4(reserve)
572
Method 1(replace): 
1768
Method 2(append)
1276
Method 3(+=)
678
Method 4(reserve)
559
Method 1(replace): 
1767
Method 2(append)
1276
Method 3(+=)
682
Method 4(reserve)
579

append 替换为push_back 与使用运算符+= 的性能相同。

【讨论】:

  • 嗨,似乎我得到了不同的结果://std::uniform_int_distribution distribution(32,97);方法1(替换):1346 方法2(追加)845 方法3(+=)1292 方法4(保留)1263
  • /GS /GL /analyze- /W3 /Gy /Zc:wchar_t /Zi /Gm- /Ox /sdl /Fd"Release\vc120.pdb" /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_LIB" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /Fa"Release\ " /EHsc /nologo /Fo"Release\"
  • 默认的最大优化(/O2)似乎与完全优化(/Ox)没有区别。以后有机会,我会在 linux 和 mac 上运行你的代码来检查差异。
猜你喜欢
  • 1970-01-01
  • 2011-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-09
  • 2013-07-07
  • 2013-01-03
相关资源
最近更新 更多