【问题标题】:move semantics slower then copy c++移动语义慢然后复制 C++
【发布时间】:2018-03-27 19:52:31
【问题描述】:

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

#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)<<"\t\t : 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。

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

【问题讨论】:

  • 发布minimal reproducible example,而不是博客文章的链接。
  • 哦,我喜欢横向滚动代码...
  • 除此之外,您正在“移动”int's。这没有任何意义。
  • 不要使用vector&lt;int&gt; 尝试使用vector&lt;vector&lt;int&gt;&gt; 之类的东西,但正如 Vittorio 所说,请注意积极优化。如果您实际上没有用所有数据计算任何东西,那么没有什么可以阻止编译器优化您的所有数据操作。

标签: c++11 vector move


【解决方案1】:

除了Vittorio's answer,让我指出导致"move" 代码路径性能下降的具体原因。

您的基准归结为这种填充向量的方式之间的区别:

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

与这种填充方式相比:

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

因为这是唯一部分,您的两个代码路径差异很大。

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

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

如果您注意减轻这两点的影响,您会发现运行时大致相同,因为正如已经指出的那样,对于int 元素,移动和复制是等效的操作。

【讨论】:

    【解决方案2】:

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

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

    • 最大的问题是你使用了int 元素的向量。 int move 在性能上等同于int copy。

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

    • 你没有在任何地方打电话给std::vector&lt;T&gt;::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);
    

    ...?

    【讨论】:

    • 我应用了你们推荐的更改,但仍然存在性能差异。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-19
    • 2012-01-15
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 2014-07-21
    相关资源
    最近更新 更多