[性能]--- string::operator+= vs. vector<char> push_back
[ performance]--- string::operator+= vs. vector<char> push_back
我正在测试这两个操作之间的性能,在 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);
)元素。
我正在测试这两个操作之间的性能,在 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);
)元素。