web-dev-qa-db-fra.com

Pourquoi emplace_back est plus rapide que Push_back?

Je pensais que emplace_back serait le gagnant, en faisant quelque chose comme ça:

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

car emplace_back construirait l'objet immédiatement dans le vecteur, tandis que Push_back, construirait d'abord un objet anonyme puis le copierait dans le vecteur. Pour plus voir this question.

Google donne également this et this questions.

J'ai décidé de les comparer pour un vecteur qui serait rempli d'entiers.

Voici le code de l'expérience:

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

Le résultat est que emplace_back est plus rapide.

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

Pourquoi? La réponse à la 1ère question liée indique clairement qu'il n'y aura pas de différence de performance.

A également essayé avec d'autres méthodes de temps de mon site pesudo, mais des résultats identiques.

[EDIT] Les commentaires disent que tester avec ints ne dit rien et que Push_back prend une réf.

J'ai fait le même test dans le code ci-dessus, mais au lieu de int j'avais une classe A:

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

Résultat:

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

[EDIT.2]

Comme l'a dit denlan, je devrais également changer la position des opérations, donc je les ai échangées et dans les deux situations (int et class A), emplace_back était de nouveau le vainqueur.

[SOLUTION]

J'exécutais le code dans debug mode, ce qui rend les mesures invalides. Pour l'analyse comparative, exécutez toujours le code dans release mode.

37
gsamaras

Votre cas de test n'est pas très utile. Push_back Prend un élément de conteneur et le copie/le déplace dans le conteneur. emplace_back Prend des arguments arbitraires et construit à partir de ceux-ci un nouvel élément conteneur. Mais si vous passez un seul argument qui est déjà de type élément à emplace_back, Vous utiliserez tout de même le constructeur copy/move.

Voici une meilleure comparaison:

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

La principale différence, cependant, est que emplace_back Effectue des conversions explicites:

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

Cet exemple sera légèrement conçu à l'avenir, quand vous devriez dire v.Push_back(std::make_unique<Foo>(1, 'x', true)). Cependant, d'autres constructions sont très sympas avec emplace aussi:

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