【问题标题】:Why emplace_back is faster than push_back?为什么 emplace_back 比 push_back 快?
【发布时间】:2014-07-06 04:44:38
【问题描述】:

我认为emplace_back 会是赢家,当做这样的事情时:

v.push_back(myClass(arg1, arg2));

因为emplace_back 将立即在向量中构造对象,而push_back 将首先构造一个匿名对象,然后将其复制到向量中。有关更多信息,请参阅this 问题。

Google 还会向thisthis 提问。

我决定将它们比较为一个将由整数填充的向量。

这里是实验代码:

#include <iostream>
#include <vector>
#include <ctime>
#include <ratio>
#include <chrono>

using namespace std;
using namespace std::chrono;

int main() {

  vector<int> v1;

  const size_t N = 100000000;

  high_resolution_clock::time_point t1 = high_resolution_clock::now();
  for(size_t i = 0; i < N; ++i)
    v1.push_back(i);
  high_resolution_clock::time_point t2 = high_resolution_clock::now();

  duration<double> time_span = duration_cast<duration<double>>(t2 - t1);

  std::cout << "push_back took me " << time_span.count() << " seconds.";
  std::cout << std::endl;

  vector<int> v2;

  t1 = high_resolution_clock::now();
  for(size_t i = 0; i < N; ++i)
    v2.emplace_back(i);
  t2 = high_resolution_clock::now();
  time_span = duration_cast<duration<double>>(t2 - t1);
  std::cout << "emplace_back took me " << time_span.count() << " seconds.";
  std::cout << std::endl;

  return 0;
}

结果是emplace_back 更快。

push_back took me 2.76127 seconds.
emplace_back took me 1.99151 seconds.

为什么?第一个链接问题的答案清楚地表明不会有性能差异。

也尝试了其他time methods,但得到了相同的结果。

[编辑] 评论说使用ints 进行测试并没有说明什么,而push_back 需要参考。

我在上面的代码中做了同样的测试,但是我有一个 A 类而不是 int

class A {
 public:
  A(int a) : a(a) {}
 private:
  int a;
};

结果:

push_back took me 6.92313 seconds.
emplace_back took me 6.1815 seconds.

[EDIT.2]

正如 denlan 所说,我也应该改变操作的位置,所以我交换了它们,在两种情况下(intclass A),emplace_back 再次成为赢家。

[解决方案]

我在debug mode 中运行代码,这使得测量值无效。对于基准测试,请始终运行 release mode 中的代码。

【问题讨论】:

  • 那么,这是否意味着您在发布模式下使用时看不到这些性能差异?你有相同的号码吗?
  • 嘿@talekeDskobeDa,我在我的旧笔记本电脑上执行此代码,所以不,抱歉。
  • 我知道原则上emplace_back 会更快——当然不会更慢。我不明白(并且在任何地方都找不到)是为什么即使在这种最简单的情况下,为什么优化器也不会产生相同的代码。有什么想法吗?
  • 不是真的@Ben,但如果你愿意,你可以发布一个新问题,链接到我的问题,确切地问这个问题(如果你愿意与我分享)。 :)

标签: c++ performance stl push-back emplace


【解决方案1】:

您的测试用例不是很有帮助。 push_back 获取一个容器元素并将其复制/移动到容器中。 emplace_back 从那些新的容器元素中获取任意参数和构造。但是如果你将一个已经是元素类型的参数传递给emplace_back,那么无论如何你都会使用复制/移动构造函数。

这里有一个更好的比较:

Foo x; Bar y; Zip z;

v.push_back(T(x, y, z));  // make temporary, push it back
v.emplace_back(x, y, z);  // no temporary, directly construct T(x, y, z) in place

然而,主要区别在于emplace_back 执行显式 转换:

std::vector<std::unique_ptr<Foo>> v;
v.emplace_back(new Foo(1, 'x', true));  // constructor is explicit!

这个例子将来会稍微做作,当你应该说v.push_back(std::make_unique&lt;Foo&gt;(1, 'x', true))时。但是,emplace 的其他构造也非常好:

std::vector<std::thread> threads;
threads.emplace_back(do_work, 10, "foo");    // call do_work(10, "foo")
threads.emplace_back(&Foo::g, x, 20, false);  // call x.g(20, false)

【讨论】:

  • 我认为您的 v.emplace_back(new Foo(1, 'x', true)); 不是异常安全的,因为它会创建新的 Foo 然后调用可能抛出的 emplace_back,从而使 Foo 泄露。
  • @Ben:是的。因此std::make_unique 提供了一个安全且不太冗长的解决方案。
猜你喜欢
  • 2021-03-11
  • 2020-09-12
  • 2012-06-09
  • 2011-05-17
  • 2013-08-18
  • 2011-10-23
  • 1970-01-01
  • 2020-06-20
相关资源
最近更新 更多