【问题标题】:Does the use of std::move have any performance benefits?使用 std::move 是否有任何性能优势?
【发布时间】:2017-03-04 17:54:40
【问题描述】:

请考虑这段代码:

#include <iostream>
#include <vector>
#include <utility>

std::vector<int> vecTest;
int main() 
{
    int someRval = 3;
    vecTest.push_back(someRval);
    vecTest.push_back(std::move(someRval));
    return 0;
}

据我了解,someRval 的值将在第一次调用push_back() 时被复制到 vecTest,但在第二次调用时 someRval 会产生一个 x 值。我的问题是,是否会有任何性能优势,我的意思是int 可能不会,但在处理更大的对象时可能会有一些性能优势吗?

【问题讨论】:

  • 转换为右值,而不是左值。对ints 没有任何影响。对于移动构造函数比复制构造函数更有效的类(例如std::string
  • 你问错问题了。如果您在概念上将对象的值移动到该向量中,则应该移动到该向量中(因此不再使用该对象)。正确地表达你的意图,性能将是它所需要的。

标签: c++ c++11 move-semantics


【解决方案1】:

移动带来的性能优势通常来自排除动态分配。

考虑一个过于简单(和幼稚)的string(缺少一个复制赋值运算符和一个移动赋值运算符):

class MyString
{
public:
    MyString() : data(nullptr) {}

    ~MyString()
    {
        delete[] data;
    }

    MyString(const MyString& other) //copy constructor
    { 
        data = new char[strlen(other.c_str()) + 1]; // another allocation
        strcpy(data, other.c_str()); // copy over the old string buffer
    }

    void set(const char* str)
    {
        char* newString = new char[strlen(str) + 1];
        strcpy(newString, str);
        delete[] data;
        data = newString;
    }

    const char* c_str() const
    {
        return data;
    }
private:
    char* data;
};

这一切都很好,但如果你的字符串变长,这里的复制构造函数可能会很昂贵。然而,复制构造函数需要复制所有内容,因为它不允许接触 other 对象,它必须完全按照它的名称执行,copy 内容。现在,如果您需要字符串的副本,这是您必须付出的代价,但如果您只想使用字符串的状态而不关心之后会发生什么,您不妨移动 它。

移动它只需要让other 对象处于一些 有效状态,这样我们就可以使用other 中的所有内容,这正是我们想要的。现在,我们要做的不是复制data 指针指向的内容,而是将我们的data 指针重新分配给other 之一,我们基本上是在窃取other 的内容,我们也好,将原来的data指针设置为nullptr

MyString(MyString&& other)
{
    data = other.data;
    other.data = nullptr;
}

好了,这就是我们所要做的。这显然比像复制构造函数那样复制整个缓冲区要快得多。

Example.

【讨论】:

  • 我知道这只是一个例子,但为了完整起见,应该在移动构造函数的开头添加一个检查,以确保 otherthis 不同,如果是这样的话它应该什么都不做。
【解决方案2】:

移动像int 甚至char* 这样的“原始”类型与复制它们没有什么不同。

复杂类型,如std::string,可以使用您愿意牺牲源对象状态的信息来使移动比复制更有效。

【讨论】:

    【解决方案3】:

    是的,但这取决于您的应用程序的详细信息 - 对象的大小和操作的频率。

    将其转换为 r 值并移动它(通过使用 std:move())可以避免复制。如果对象的大小足够大,则可以节省时间(例如,考虑一个具有 1 000 000 个双精度的数组 - 复制它通常意味着复制 4 MB 或更多 MB 的内存)。
    另一点是频率——如果你的代码经常执行相应的操作,它可以加起来相当可观。

    请注意,源对象在此过程中被销毁(变得无法使用),这可能会或可能不会被您的逻辑所接受 - 您需要理解它并相应地编写代码。如果之后您仍然需要源对象,它显然不会工作。

    一般情况下,除非需要优化,否则不要优化。

    【讨论】:

      猜你喜欢
      • 2011-06-11
      • 2011-12-18
      • 1970-01-01
      • 2015-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多