3

这段代码

#include <iostream>
#include <vector>

struct obj
{
  std::string name;
  int age;
  float money;

  obj():name("NO_NAME"),age(0),money(0.0f){}
  obj(const std::string& _name, const int& _age, const float& _money):name(_name),age(_age),money(_money){}

  obj(obj&& tmp): name(tmp.name), age(tmp.age), money(tmp.money) {}
  obj& operator=(obj&&) {return *this;}

};

int main(int argc, char* argv[])
{
  std::vector<obj> v;
  for( int i = 0; i < 5000000; ++i )
  {
    v.emplace_back(obj("Jon", 45, 500.6f));
  }
  return(0);
}

比同等速度慢大约 2 倍v.push_back(obj("Jon", 45, 500.6f));,我不明白为什么。

我已经用 botg++ 4.7.2clang++ 3.3.

我哪里错了?


现在我已经更正了我的移动构造函数,我将添加更多

这是一个 push_back 版本

这是 emplace_back 版本

我正在使用timeLinux 下的实用程序测试这 2 并使用

g++-4.7 -std=c++11 -s -O3 -DNDEBUG

或者

clang++ -std=c++11 -s -O3 -DNDEBUG
4

5 回答 5

5

什么都不做比较好。您试图使其更快(比什么更快?在编写移动构造函数之前您实际上进行了分析吗?),但是您破坏了它。

编译器免费生成复制和移动构造函数和赋值运算符,而且她做得对。通过决定自己编写,你是在告诉编译器你知道得更好,所以她只是让开,让你自己改进破解它。

你破坏的第一件事是你让你的移动构造函数实际上是copy。有名字的东西是左值,即使是右值引用,左值也不能隐式移动。所以初始化器需要实际调用std::move.

您破坏的第二件事是您没有使移动构造函数声明它不会通过添加noexcept来抛出。生成的编译器有这个。通过不声明不抛出异常,std::vector在重新分配底层存储时, 的实现可能不会使用移动:如果不保证移动不会抛出,它就无法提供强大的异常保证。

做这一切会让它表现更好吗?也许。也许不吧。您的实现可能正在对 进行小字符串优化std::string,这意味着没有动态分配:整个字符串"Jon"很小,将直接存储在std::string对象中。这使得移动具有与复制相同的成本。

您可以通过动态分配整个obj结构并使用unique_ptr. 这将使移动比副本便宜,即使存在小字符串优化也是如此。但是,您正在为分配成本和额外的间接成本付出代价。这是否可取,只有您可以判断。

于 2012-11-20T13:30:22.630 回答
4

您应该将数据从参数移动到移动构造函数:

obj(obj&& tmp)
: 
name(std::move(tmp.name)), age(std::move(tmp.age)), money(std::move(tmp.money)) {}

虽然如果你正确使用这应该是无关紧要emplace_back的。

于 2012-11-20T13:16:57.507 回答
2

代替

v.emplace_back(obj("Jon", 45, 500.6f));

尝试

v.emplace_back("Jon", 45, 500.6f);

push_back具有启用移动的重载。emplace_back用于就地构建。

编辑:R. Martinho Fernandes 所说的。:)

obj(obj&& tmp): name(std::move(tmp.name)), age(std::move(tmp.age)), money(std::move(tmp.money)) {}
于 2012-11-20T13:09:28.023 回答
2

这可能是你想要的:

struct obj
{
  std::string name;
  int age;
  float money;

  obj()
      : name("NO_NAME")
      , age(0)
      , money(0.0f)
  {
  }

  obj(std::string _name, int _age, float _money)
      : name(std::move(_name))
      , age(std::move(_age))
      , money(std::move(_money))
  {
  }
};

int main(int argc, char* argv[])
{
  std::vector<obj> v;
  for( int i = 0; i < 5000000; ++i )
  {
    v.emplace_back("Jon", 45, 500.6f);
  }
  return(0);
}

请注意,我将您的obj(std::string _name, int _age, float _money)构造函数更改为移动_name,而不是对其进行不必要的复制。

你也叫emplace_back错了,应该是emplace_back("Jon", 45, 500.6f)

所有其他的东西都是由编译器自动优化生成的。

于 2012-11-20T13:15:55.967 回答
2

运行时间主要取决于从字符串文字构造 std::string ,因此 move 构造和 emplace 构造之间的区别是微不足道的。

这在我的机器上需要 400 毫秒:

#include <iostream>
#include <vector>

using namespace std;

struct obj
{
    string name;
    int age;
    float money;
};

int main(int argc, char* argv[])
{
    vector<obj> v;
    for( int i = 0; i < 5000000; ++i )
    {
        v.emplace_back(obj{"Jon", 45, 500.6f});
    }
    return v.size();
}

这在我的机器上需要 80 毫秒:

#include <iostream>
#include <vector>

using namespace std;

struct obj
{
    int age;
    float money;
};

int main(int argc, char* argv[])
{
    vector<obj> v;
    for( int i = 0; i < 5000000; ++i )
    {
        v.emplace_back(obj{45, 500.6f});
    }
    return v.size();
}

请注意,普通结构将为其生成一个合理的默认移动构造函数。

这已经在我的机器上花费了 220 毫秒:

#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char* argv[])
{
    int t = 0;
    for( int i = 0; i < 5000000; ++i )
    {
        string s("Jon");
        t += s.size();
    }
    return t;
}
于 2012-11-20T13:41:57.900 回答