[ 性能]--- 字符串::运算符+= 与向量<char>push_back

[ performance]--- string::operator+= vs. vector<char> push_back

本文关键字:char lt gt back push 向量 性能 字符串 运算符      更新时间:2023-10-16

我测试了这两个操作之间的性能,在G++4.7.3上,string::operator+=版本的速度大约快2倍。我的简单测试:造成如此巨大差异的原因是什么?

g++-O2-std=c++11

#include <iostream>
#include <ctime>
#include <string>
#include <vector>
using namespace std;
class Timer {
    public:
    Timer(const std::string &label)
        :label_(label)
    {
        begin_clock_ = clock();
        cout <<label<<"- Timer starts!"<<endl;
    }
    ~Timer() {
        clock_t clock_used = clock() - begin_clock_;
        cout<<label_<<"- Clock used:"<<clock_used
        <<" Time:"<<clock_used/CLOCKS_PER_SEC<<endl;
    }
    private:
    clock_t begin_clock_;
    string label_;
};
int str(int loop)
{
    Timer t("str");
    string s;
    for(int i=0;i<loop;++i)
        s+=(i%2);
    return s.length();
}
int vec(int loop)
{
    Timer t("vec");
    vector<bool> v;
    for(int i=0;i<loop;++i)
    v.push_back(i%2);
    return v.size();
}
int main()
{
    int loop = 1000000000;
    int s1=str(loop);
    int s2=vec(loop);
    cout <<"s1="<<s1<<endl;
    cout <<"s2="<<s2<<endl;
}

字符串和向量都连续存储其内容。如果没有足够的空间添加新元素,则必须增加容量(内存分配),并且必须将现有内容移动到新位置。

因此,性能应该在很大程度上取决于实现的分配策略。如果一个容器在当前容量耗尽时保留更大的块,它将更高效(分配更少,移动更少)。

当然,结果取决于实施。例如,在我的测试中,矢量实现比字符串变体快三分之一。

这里如何看到效果:

int str(int loop)
{
    Timer t("str");
    string s;
    size_t capa = 0, ncapa, alloc = 0;   // coutners for monitoring allocations
    long long mw = 0;                    // 
    for(int i = 0; i<loop; ++i){
        if((ncapa = s.capacity()) != capa)  // check if capacity increased
        {                                              // 
            capa = ncapa; alloc++; mw += s.size();     //
        }                                              //
        s += (i % 2);
    }
    cout << "allocations: " << alloc << " and elements moved: " << mw << endl;
    return s.length();
}

例如,在我的编译器上,对于字符串,我得到了2、4、8。。。对于矢量,它立即开始于32,64。。。

现在,这并不能解释一切。如果您想了解性能的哪些部分来自分配策略,哪些来自其他因素,可以在开始添加任何元素之前预先分配字符串(s.reserve(loop);)和向量(v.reserve(loop);)。