c++ - ¿Por qué emplace_back es más rápido que push_back?
performance stl (1)
Pensé que emplace_back
sería el ganador, al hacer algo como esto:
v.push_back(myClass(arg1, arg2));
porque emplace_back
construiría el objeto inmediatamente en el vector, mientras que push_back
, primero construiría un objeto anónimo y luego lo copiaría al vector. Para más información vea this pregunta.
Google también da this y this preguntas.
Decidí compararlos para un vector que sería llenado por enteros.
Aquí está el código del experimento:
#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;
}
El resultado es que emplace_back
es más rápido.
push_back took me 2.76127 seconds.
emplace_back took me 1.99151 seconds.
¿Por qué? La respuesta de la primera pregunta vinculada claramente dice que no habrá diferencia de rendimiento.
También probé con otros métodos de tiempo de mi pesudo-sitio, pero con resultados idénticos.
[EDITAR] Los comentarios dicen que las pruebas con int
s no dicen nada y que push_back
toma una referencia.
Hice la misma prueba en el código anterior, pero en lugar de int
tuve una clase A
:
class A {
public:
A(int a) : a(a) {}
private:
int a;
};
Resultado:
push_back took me 6.92313 seconds.
emplace_back took me 6.1815 seconds.
[EDIT.2]
Como dijo Denlan, también debería cambiar la posición de las operaciones, así que las cambié y en ambas situaciones ( int
y class A
), emplace_back
fue nuevamente el ganador.
[SOLUCIÓN]
Estaba ejecutando el código en debug mode
, lo que hace que las mediciones no sean válidas. Para la evaluación comparativa, siempre ejecute el código en release mode
.
Su caso de prueba no es muy útil. push_back
toma un elemento contenedor y lo copia / mueve al contenedor. emplace_back
toma argumentos y construcciones arbitrarios de esos un nuevo elemento contenedor. Pero si pasa un solo argumento que ya es de tipo elemento a emplace_back
, simplemente usará el constructor copiar / mover de todos modos.
Aquí hay una mejor comparación:
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 diferencia clave, sin embargo, es que emplace_back
realiza conversiones explícitas :
std::vector<std::unique_ptr<Foo>> v;
v.emplace_back(new Foo(1, ''x'', true)); // constructor is explicit!
Este ejemplo se v.push_back(std::make_unique<Foo>(1, ''x'', true))
ligeramente en el futuro, cuando debería decir v.push_back(std::make_unique<Foo>(1, ''x'', true))
. Sin embargo, otras construcciones también son muy agradables con 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)