使用std :: move进行就地置换的性能

时间:2012-03-22 12:11:10

标签: c++ c++11

我写了一个就地排列算法[TAOCP3中的一个练习],其中内循环是

template<typename T>
void inplace_permute(T *pT, int *P, const int n)
{
  // part of the inner loop
  pT[j] = std::move(pT[k]);
  P[j] = j;

  // more logic to update j, k, etc.
}

此处,pT是要排序的元素数组,P是排列表,n是元素数。

如果T是复杂类型,例如字符串,std::move会提高性能吗?同样重要的是,如果T是基本类型(例如,int?)

,它是否可以被优化

2 个答案:

答案 0 :(得分:3)

我将把你的问题总结为:

  

std::move做了什么?

基本上,std::move可以使用Move Constructor和Move Assignment Operator。

通常,它们接近按位副本(它们正好一个),因此性能通常与类sizeof相关。

因此,如果std::move(someint)std::move(somestring)具有相似的大小,即使其中一个是内置的,另一个是用户类,也会有类似的性能。

虽然存在一些差异。

  • 在内置,移动只是一个按位复制。由于未指定移动的值,因此不需要调零。移动后,您可能希望为其分配已知值(0或其他)
  • 在用户类上,通常有资源(这种动态分配的缓冲区),移动意味着:清理移动到的实例(用于分配),进行有点按位复制,重置移动 - 来自实例。所以还有一些工作要做。

要理解,我们可以通过示例字符串实现来说明这一点:

class String {
public:
  // Many things
  String(String&& right);
  String& operator=(String right);

  friend void swap(String& left, String& right);

private:
  // On 64 bits platform, 4x as big as an `int`
  size_t capacity;
  size_t size;
  char* buffer;
};

// Move Constructor
String::String(String&& right):
   capacity(right.capacity), size(right.size), buffer(right.buffer)
{
  right = String(); // reset right
}


// Assignment Operator
String& String::operator=(String right) {
  swap(*this, right);
  return *this;
}

// Swap
void swap(String& left, String& right) {
  using std::swap;
  swap(left.capacity, right.capacity);
  swap(left.size    , right.size);
  swap(left.buffer  , right.buffer);
}

如您所见,作业pT[j] = std::move(pT[k]);表示(语义上):

  • 创建临时(制作pT[k]的按位副本)
  • 重置pT[k]
  • 在临时和pT[j]
  • 之间交换状态
  • 销毁临时(通常会释放从pT[j]继承的存储)

编译器应该或多或少地能够将其优化为:

  • pT[j]pT[k]
  • 之间交换状态
  • 销毁pT[k](只是释放存储空间)
  • pT[k]
  • 中重建新实例

或粗略地说:

swap(ptj, ptk);    // swap 3 fields
delete ptk.buffer; // might be a no-op
ptk = String();    // 0-out 3 fields

注意:这是一个玩具实现,它在gcc上会更简单一点,在VC ++上要复杂得多,因为它们使用不同的数据表示。

答案 1 :(得分:1)

如果T有一个移动赋值运算符,那么它将用于rvalues并可能提高性能。

如果类型没有移动分配,但确实有复制分配(如int),则会复制该值。