为什么emplace_back比push_back更快?

时间:2021-08-25 04:15:38

I thought that emplace_back would be the winner, when doing something like this:

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

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

because emplace_back would construct the object immediately in the vector, while push_back, would first construct an anonymous object and then would copy it to the vector. For more see this question.

因为emplace_back将在vector中立即构造对象,而push_back则首先构造一个匿名对象,然后将其复制到vector中。想要更多地了解这个问题。

Google also gives this and this questions.

谷歌也给出了这个和这个问题。

I decided to compare them for a vector that would be filled by integers.

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

Here is the experiment code:

实验代码如下:

#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;
}

The result is that emplace_back is faster.

结果是emplace_back更快。

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

Why? The answer of the 1st linked question clearly says that there will be no performance difference.

为什么?第一个相关问题的答案清楚地表明,没有任何性能差异。

Also tried with other time methods from my pesudo-site, but identical results.

也尝试了其他时间方法从我的pesudo网站,但相同的结果。

[EDIT] Comments say that testing with ints doesn't say anything and that push_back takes a ref.

[编辑]评论说使用ints的测试不会说任何东西,而push_back则采用ref。

I did the same test in the code above, but instead of int I had a class A:

我在上面的代码中做了同样的测试,但是我没有使用a类:

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

Result:

结果:

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

[EDIT.2]

(EDIT.2)

As denlan said, I should also change the position of the operations, so I swapped them and in both situation (int and class A), emplace_back was again the winner.

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

[SOLUTION]

(解决方案)

I was running the code in debug mode, which makes the measurements invalid. For benchmarking, always run the code in release mode.

我在调试模式下运行代码,这使得测量无效。对于基准测试,总是在发布模式下运行代码。

1 个解决方案

#1


37  

Your test case isn't very helpful. push_back takes a container element and copies/moves it into the container. emplace_back takes arbitrary arguments and constructs from those a new container element. But if you pass a single argument that's already of element type to emplace_back, you'll just use the copy/move constructor anyway.

你的测试用例没有多大帮助。push_back采用容器元素并将其复制/移动到容器中。emplace_back接受来自这些新容器元素的任意参数和构造。但是,如果您传递一个已经由元素类型到emplace_back的参数,那么您将只使用copy/move构造函数。

Here's a better comparison:

这是一个更好的比较:

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

The key difference, however, is that emplace_back performs explicit conversions:

但是,关键的区别在于emplace_back执行显式转换:

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

This example will be mildly contrived in the future, when you should say v.push_back(std::make_unique<Foo>(1, 'x', true)). However, other constructions are very nice with emplace, too:

这个例子在将来会被委婉地表达出来,你应该说v。push_back方法(std::make_unique < Foo >(1“x”真正))。然而,其他的建筑也很好与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)

#1


37  

Your test case isn't very helpful. push_back takes a container element and copies/moves it into the container. emplace_back takes arbitrary arguments and constructs from those a new container element. But if you pass a single argument that's already of element type to emplace_back, you'll just use the copy/move constructor anyway.

你的测试用例没有多大帮助。push_back采用容器元素并将其复制/移动到容器中。emplace_back接受来自这些新容器元素的任意参数和构造。但是,如果您传递一个已经由元素类型到emplace_back的参数,那么您将只使用copy/move构造函数。

Here's a better comparison:

这是一个更好的比较:

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

The key difference, however, is that emplace_back performs explicit conversions:

但是,关键的区别在于emplace_back执行显式转换:

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

This example will be mildly contrived in the future, when you should say v.push_back(std::make_unique<Foo>(1, 'x', true)). However, other constructions are very nice with emplace, too:

这个例子在将来会被委婉地表达出来,你应该说v。push_back方法(std::make_unique < Foo >(1“x”真正))。然而,其他的建筑也很好与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)