He estado comparando una implementación STL de una popular biblioteca XmlRpc con una implementación que sobre todo evita STL. La implementación de STL es mucho más lenta: obtuve 47s hasta 4.5s. He diagnosticado algunas de las razones: en parte se debe a que std :: string no se usó correctamente (por ejemplo, el autor debería haber utilizado "const std :: string &" siempre que sea posible; no use std :: string como si eran cadenas de Java), pero también es porque cada vez que el vector sobrepasaba sus límites, se llamaban continuamente a los constructores de copias, lo cual era muy frecuente. Los constructores de copias fueron muy lentos porque hicieron copias en profundidad de los árboles (de valores XmlRpc).std :: vector en VisualStudio2008 parece estar implementado de manera subóptima: demasiadas llamadas al constructor de copias
Otra persona me dijo en StackOverflow que las implementaciones de std :: vector suelen duplicar el tamaño del búfer cada vez que superan. Este no parece ser el caso en VisualStudio 2008: para agregar 50 elementos a un estándar, el vector tomó 177 llamadas del constructor de copia. Doblando cada vez debe llamar al constructor de copia 64 veces. Si estaba muy preocupado por mantener bajo el uso de la memoria, entonces aumentar en un 50% cada vez debería llamar al constructor de copias 121 veces. Entonces, ¿de dónde viene el 177?
Mi pregunta es: (a) ¿por qué se llama al constructor de copias con tanta frecuencia? (b) ¿hay alguna manera de evitar el uso del constructor de copia si solo está moviendo un objeto de una ubicación a otra? (En este caso, y de hecho la mayoría de los casos, una memcpy() hubiera sido suficiente, y esto marca una GRAN diferencia).
(NB: Yo sé de vector :: reserva(), estoy un poco decepcionado de que los programadores de aplicaciones se necesitan para poner en práctica el truco duplicación cuando algo como esto ya es parte de cualquier buena aplicación STL.)
Mi programa de pruebas:
#include <string>
#include <iostream>
#include <vector>
using namespace std;
int constructorCalls;
int assignmentCalls;
int copyCalls;
class C {
int n;
public:
C(int _n) { n = _n; constructorCalls++; }
C(const C& orig) { copyCalls++; n = orig.n; }
void operator=(const C &orig) { assignmentCalls++; n = orig.n; }
};
int main(int argc, char* argv[])
{
std::vector<C> A;
//A.reserve(50);
for (int i=0; i < 50; i++)
A.push_back(i);
cout << "constructor calls = " << constructorCalls << "\n";
cout << "assignment calls = " << assignmentCalls << "\n";
cout << "copy calls = " << copyCalls << "\n";
return 0;
}
Esto se ha solucionado en C++ 11, ejecuta _MUCH_ más rápido ahora –