web-dev-qa-db-ja.com

emplace_backがPush_backより速いのはなぜですか?

このようなことをすると、emplace_backが勝者になると思いました。

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

emplace_backはベクター内ですぐにオブジェクトを作成し、Push_backは最初に匿名オブジェクトを作成し、次にそれをベクターにコピーするためです。詳細については、 this questionを参照してください。

また、Googleでは this および this の質問も提供しています。

整数で埋められるベクトルについてそれらを比較することにしました。

実験コードは次のとおりです。

#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メソッド で私の擬似サイトから試しましたが、結果は同じです。

[編集]コメントは、intsでテストしても何も言わず、Push_backはrefを取ると言います。

上記のコードで同じテストを行いましたが、intの代わりにクラスAがありました。

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.

[編集2]

Denlanが言ったように、操作の位置も変更する必要があるため、それらを交換し、両方の状況(intclass A)で、emplace_backが再び勝者になりました。

[溶液]

debug modeでコードを実行していたため、測定が無効になりました。ベンチマークのために、release modeのコードを常に実行します。

37
gsamaras

テストケースはあまり役に立ちません。 _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_がexplicit変換を実行することです:

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

この例は、将来、v.Push_back(std::make_unique<Foo>(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)
_
49
Kerrek SB