移动语义的速度要慢于复制c++



我写了一个比较移动和测试语义的小测试:

#include <vector>
#include <iostream>
#include <iterator>
#include <chrono>
#include <iomanip>
using namespace std;
int main()
{
int lenLeft = 3;
int lenMid = 3;
vector<int> lenVec{10,100,1000,static_cast<int>(1e4),static_cast<int>(1e5),static_cast<int>(1e6),static_cast<int>(1e7)};
int reps = 100;
vector<double>delta_t_move;
vector<double>delta_t_copy;

//move
cout<<"move"<<endl;
{
for(int len : lenVec)
{
auto startTime = std::chrono::high_resolution_clock::now();
for(int i = 0; i<reps;i++)
{
vector<int> leftVec(len,0);
vector<int> rightVec;
move(leftVec.begin()+lenLeft+lenMid,leftVec.end(),std::back_inserter(rightVec));
leftVec.erase(leftVec.begin()+lenLeft+lenMid,leftVec.end());
vector<int> midVec;
std::move(leftVec.begin()+lenLeft,leftVec.begin()+lenLeft+lenMid,std::back_inserter(midVec));
leftVec.erase(leftVec.begin()+lenLeft,leftVec.begin()+lenLeft+lenMid);
}
auto endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = endTime - startTime;
delta_t_move.push_back(elapsed.count());
}
}
//copy
cout<<"copy"<<endl;
{
for(int len : lenVec)
{
auto startTime = std::chrono::high_resolution_clock::now();
for(int i = 0; i<reps;i++)
{
vector<int> leftVec(len,0);
vector<int> rightVec = vector<int>(leftVec.begin()+lenLeft+lenMid,leftVec.end());
leftVec.erase(leftVec.begin()+lenLeft+lenMid,leftVec.end());
vector<int> midVec = vector<int>(leftVec.begin()+lenLeft,leftVec.begin()+lenLeft+lenMid);
leftVec.erase(leftVec.begin()+lenLeft,leftVec.begin()+lenLeft+lenMid);
}
auto endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = endTime - startTime;
delta_t_copy.push_back(elapsed.count());
}
}
for(int i = 0; i<lenVec.size();i++)
{
cout<<"lenVec = "<<setw(40)<<lenVec.at(i)<<"tt : delta_t_copy/delta_t_move = "<< delta_t_copy.at(i)/delta_t_move.at(i)<<endl;
}

return 0;
}

这个程序的输出是:

move
copy
lenVec = 10 : delta_t_copy/delta_t_move = 0.431172
lenVec = 100 : delta_t_copy/delta_t_move = 0.257102
lenVec = 1000 : delta_t_copy/delta_t_move = 0.166006
lenVec = 10000 : delta_t_copy/delta_t_move = 0.108573
lenVec = 100000 : delta_t_copy/delta_t_move = 0.113769
lenVec = 1000000 : delta_t_copy/delta_t_move = 0.134912
lenVec = 10000000 : delta_t_copy/delta_t_move = 0.133874

我把一个大小为len的初始向量分成3块。长度为3的第一块、长度为3中的一块和尺寸为len-6的其余部分。我的结果表明,复制语义比移动语义快得多。

我正在使用MSVC2015。

你知道这怎么可能是真的吗?在什么情况下移动语义更快?

除了Vittorio的回答之外,让我指出"move"代码路径中性能下降的具体原因。

你的基准测试可以归结为这种填充矢量的方法之间的区别:

vector<int> rightVec = vector<int>(startIt, endIt);

与这种填充方式相比:

vector<int> rightVec;
move(startIt, endIt, std::back_inserter(rightVec));

因为这是唯一的部分,其中您的两个代码路径有很大不同。

第二个版本预计会更慢,原因有两个:

  • 在第二种情况下,目标向量不知道它应该存储多少元素,因此它必须在执行插入时不断增长。增长向量是昂贵的,因为它涉及到先前插入的元素的重新分配和复制/移动。您可以通过在move之前插入适当的reseve()调用来消除这一缺点。这将大大降低此代码路径的性能损失
  • 剩下的小性能差异将是由于您移动到back_inserter,这将导致对目标向量的逐元素插入,就像在第一种情况下执行的批量插入一样

如果您注意减轻这两点的影响,您将观察到运行时大致相等,因为正如已经指出的,移动和复制是int元素的等效操作。

您的基准测试存在缺陷且不精确。

  • 首先,您从未提及您使用的编译器标志-您启用了优化吗?

  • 最大的问题是使用int元素的向量。就性能而言,int移动相当于int复制

  • 为了获得一个正确和完整的基准测试,您还应该尝试g++和clang++。

  • 您不会在任何地方拨打std::vector<T>::reserve

  • 虽然不太可能,但不能保证编译器不会积极优化循环(例如,将多个循环展开或融合在一起)


您还在"复制"基准中执行一些不必要的移动:

vector<int> rightVec = vector<int>(leftVec.begin()+lenLeft+lenMid,leftVec.end());    
vector<int> midVec = vector<int>(leftVec.begin()+lenLeft,leftVec.begin()+lenLeft+lenMid);

为什么不。。。

vector<int> rightVec(leftVec.begin()+lenLeft+lenMid,leftVec.end());    
vector<int> midVec(leftVec.begin()+lenLeft,leftVec.begin()+lenLeft+lenMid);

最新更新