web-dev-qa-db-de.com

Warum ist emplace_back schneller als Push_back?

Ich dachte, dass emplace_back Der Gewinner sein würde, wenn ich so etwas mache:

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

weil emplace_back das Objekt sofort im Vektor erstellen würde, während Push_back zuerst ein anonymes Objekt erstellen und es dann in den Vektor kopieren würde. Weitere Informationen finden Sie unter this question.

Google gibt auch this und this Fragen.

Ich entschied mich, sie für einen Vektor zu vergleichen, der mit ganzen Zahlen gefüllt sein würde.

Hier ist der Testcode:

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

Das Ergebnis ist, dass emplace_back Schneller ist.

Push_back took me 2.76127 seconds.
emplace_back took me 1.99151 seconds.

Warum? Die Antwort auf die erste verknüpfte Frage besagt eindeutig, dass es keinen Leistungsunterschied geben wird.

Auch mit anderen Zeitmethoden von meiner Pesudo-Seite aus versucht, aber identische Ergebnisse.

[EDIT] Kommentare besagen, dass das Testen mit ints nichts aussagt und dass Push_back Eine Referenz benötigt.

Ich habe den gleichen Test im obigen Code durchgeführt, aber anstelle von int hatte ich eine Klasse A:

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

Ergebnis:

Push_back took me 6.92313 seconds.
emplace_back took me 6.1815 seconds.

[EDIT.2]

Wie Denlan sagte, sollte ich auch die Position der Operationen ändern, also habe ich sie getauscht und in beiden Situationen (int und class A) War emplace_back Wieder der Gewinner.

[LÖSUNG]

Ich habe den Code in debug mode Ausgeführt, wodurch die Messungen ungültig werden. Führen Sie zum Benchmarking immer den Code in release mode Aus.

37
gsamaras

Ihr Testfall ist nicht sehr hilfreich. Push_back Nimmt ein Containerelement und kopiert/verschiebt es in den Container. emplace_back Akzeptiert beliebige Argumente und konstruiert daraus ein neues Containerelement. Wenn Sie jedoch ein einzelnes Argument, das bereits vom Typ Element ist, an emplace_back Übergeben, verwenden Sie einfach den Konstruktor copy/move.

Hier ist ein besserer Vergleich:

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

Der Hauptunterschied ist jedoch, dass emplace_backexplizit Konvertierungen durchführt:

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

Dieses Beispiel wird in Zukunft leicht erfunden, wenn Sie v.Push_back(std::make_unique<Foo>(1, 'x', true)) sagen sollten. Andere Konstruktionen sind jedoch auch mit emplace sehr schön:

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