C++ 在已知大小时向向量添加元素的基准测试
我已经做了一个很小的基准,用于向向量添加新元素,我知道它的大小 代码:C++ 在已知大小时向向量添加元素的基准测试,c++,c++11,vector,benchmarking,push-back,C++,C++11,Vector,Benchmarking,Push Back,我已经做了一个很小的基准,用于向向量添加新元素,我知道它的大小 代码: struct foo{ foo() = default; foo(double x, double y, double z) :x(x), y(y), z(y){ } double x; double y; double z; }; void resize_and_index(){ std::vector<foo> bar(1000); for
struct foo{
foo() = default;
foo(double x, double y, double z) :x(x), y(y), z(y){
}
double x;
double y;
double z;
};
void resize_and_index(){
std::vector<foo> bar(1000);
for (auto& item : bar){
item.x = 5;
item.y = 5;
item.z = 5;
}
}
void reserve_and_push(){
std::vector<foo> bar;
bar.reserve(1000);
for (size_t i = 0; i < 1000; i++)
{
bar.push_back(foo(5, 5, 5));
}
}
void reserve_and_push_move(){
std::vector<foo> bar;
bar.reserve(1000);
for (size_t i = 0; i < 1000; i++)
{
bar.push_back(std::move(foo(5, 5, 5)));
}
}
void reserve_and_embalce(){
std::vector<foo> bar;
bar.reserve(1000);
for (size_t i = 0; i < 1000; i++)
{
bar.emplace_back(5, 5, 5);
}
}
resize_and_index: 176 mSec
reserve_and_push: 560 mSec
reserve_and_push_move: 574 mSec
reserve_and_embalce: 143 mSec
调用代码:
const size_t repeate = 100000;
auto start_time = clock();
for (size_t i = 0; i < repeate; i++)
{
resize_and_index();
}
auto stop_time = clock();
std::cout << "resize_and_index: " << (stop_time - start_time) / double(CLOCKS_PER_SEC) * 1000 << " mSec" << std::endl;
start_time = clock();
for (size_t i = 0; i < repeate; i++)
{
reserve_and_push();
}
stop_time = clock();
std::cout << "reserve_and_push: " << (stop_time - start_time) / double(CLOCKS_PER_SEC) * 1000 << " mSec" << std::endl;
start_time = clock();
for (size_t i = 0; i < repeate; i++)
{
reserve_and_push_move();
}
stop_time = clock();
std::cout << "reserve_and_push_move: " << (stop_time - start_time) / double(CLOCKS_PER_SEC) * 1000 << " mSec" << std::endl;
start_time = clock();
for (size_t i = 0; i < repeate; i++)
{
reserve_and_embalce();
}
stop_time = clock();
std::cout << "reserve_and_embalce: " << (stop_time - start_time) / double(CLOCKS_PER_SEC) * 1000 << " mSec" << std::endl;
为什么我会得到这些结果?什么使emplace_back优于
其他人
您得到这些结果是因为您对其进行了基准测试,并且必须得到一些结果:)在这种情况下,Emplace back做得更好,因为它直接在向量保留的内存位置创建/构造对象。因此,它不必首先在外部创建对象(可能是临时对象),然后将其复制/移动到向量的保留位置,从而节省一些开销 为什么std::move会使性能稍差 如果你问为什么它比emplace更昂贵,那是因为它必须“移动”对象。在这种情况下,移动操作可以很好地简化为复制。因此,一定是复制操作花费了更多的时间,因为此复制不是针对emplace案例进行的。
您可以尝试挖掘生成的汇编代码,看看到底发生了什么。
另外,我不认为将其余函数与“resize_和_index”进行比较是公平的。在其他情况下,对象可能被实例化多次。首先,reserve_和_push以及reserve_和_push_move在语义上是等价的。您构造的临时foo已经是一个右值(push_-back的右值引用重载已经被使用);在移动中包装它不会改变任何东西,除了可能会模糊编译器的代码,这可以解释轻微的性能损失。(尽管我认为这更可能是噪声)此外,您的类具有相同的复制和移动语义 其次,如果将循环的主体编写为
item = foo(5, 5, 5);
尽管只有分析才能显示这一点。问题是编译器可能会为三个单独的赋值生成次优代码
第三,你也应该试试这个:
std::vector<foo> v(100, foo(5, 5, 5));
向量v(100,foo(5,5,5));
第四,这个基准测试对编译器非常敏感,因为编译器意识到这些函数实际上都没有做任何事情,只是简单地优化了它们的完整体
现在进行分析。注意,如果您真的想知道发生了什么,您必须检查编译器生成的程序集
第一个版本执行以下操作:
最后,除非代码真正对性能敏感,否则应该编写可读性最好的版本。(这是我的版本获胜的另一个地方,依我看。)我不确定预备队和推、预备队和推的区别是否只是噪音。我使用g++4.8.4做了一个简单的测试,注意到可执行文件大小/附加汇编指令的增加,即使理论上在这种情况下,编译器可以忽略std::move。使用哪些编译器和编译器参数执行此测试?哪些编译器参数?尝试设置/O2优化标志。再次编辑它是O2,用于比较:VS2013、Win7、Xeon 1241@3.5 Ghz调整大小和索引:144毫秒保留和推送:199毫秒保留和推送移动:201毫秒保留和保存:111毫秒CGCC 4.8,
-O3
:resize_and_index:215.873 mSec reserve_and_push:284.444 mSec reserve_and_push_move:279.426 mSec reserve_and_embalce:252.87 mSec-因此,在机器(运行Lubuntu 14.04的VMVare)、体系结构(一些i3 x64)和编译器中,结果非常复杂
std::vector<foo> v(100, foo(5, 5, 5));