2011-11-02 22 views
13

Estoy tratando de comparar el rendimiento entre punteros sin formato, impulsar shared_ptr y boost weak_ptr. En la parte de desreferenciación, esperaba que shared_ptr y raw_ptr fueran iguales, pero los resultados muestran que shared_ptr es aproximadamente dos veces más lento. Para la prueba, estoy creando una matriz con cualquiera de punteros o punteros compartidos a enteros, y luego eliminación de referencias en un bucle como este:Boost shared_ptr desreferencia costo

int result; 
for(int i = 0; i != 100; ++i) 
{ 
    for(int i = 0; i != SIZE; ++i) 
    result += *array[i]; 
} 

El código completo para la prueba se puede encontrar aquí: https://github.com/coolfluid/coolfluid3/blob/master/test/common/utest-ptr-benchmark.cpp

tiempos de prueba para una construcción optimizada sin afirmaciones se pueden encontrar aquí: http://coolfluidsrv.vki.ac.be/cdash/testDetails.php?test=145592&build=7777

Los valores de interés son "tiempo DerefShared" y "tiempo DerefRaw"

Supongo que la prueba puede ser defectuosa de alguna manera, pero no pude deducir de dónde viene la diferencia. La creación de perfiles muestra que el operador * de shared_ptr se incluye, parece que lleva más tiempo. Comprobé dos veces que la aserción de impulso está desactivada.

Estaría muy agradecido si alguien puede explicar de dónde podría venir la diferencia.

independiente adicional de prueba: https://gist.github.com/1335014

+0

+1, yo estaba pensando en esto el otro día también. –

+7

¿Efectos de caché? shared_ptr es más grande que un puntero sin formato, por lo que su matriz cubrirá más líneas de caché y tardará más en leerse. –

+0

¿En qué plataforma está ejecutando estas pruebas? – SoapBox

Respuesta

10

Como se ha dicho Alan Stokes en su comentario, esto es debido a los efectos de caché. Los punteros compartidos incluyen un recuento de referencias, lo que significa que son físicamente más grandes en memoria que un puntero sin formato. Cuando se almacena en una matriz contigua, obtiene menos punteros por línea de caché, lo que significa que el ciclo tiene que salir a la memoria principal con más frecuencia de lo que lo haría con un puntero sin formato.

se puede observar este comportamiento, en su prueba puntero del crudo, la asignación de SIZE*2 enteros, sino también cambiar el bucle de-referencia a zancadas por i+=2 en lugar de ++i. Hacer esto arrojó aproximadamente los mismos resultados en mis pruebas. Mi código para la prueba en bruto está debajo.

#include <iostream> 
#include <boost/timer.hpp> 

#define SIZE 1000000 

typedef int* PtrT; 

int do_deref(PtrT* array) 
{ 
    int result = 0; 
    for(int i = 0; i != 1000; ++i) 
    { 
     for(int i = 0; i != SIZE*2; i+=2) 
      result += *array[i]; 
    } 

    return result; 
} 

int main(void) 
{ 
    PtrT* array = new PtrT[SIZE*2]; 
    for(int i = 0; i != SIZE*2; ++i) 
     array[i] = new int(i); 
    boost::timer timer; 
    int result = do_deref(array); 
    std::cout << "deref took " << timer.elapsed() << "s" << std::endl; 
    return result; 
} 

Incidentalmente, usando boost::make_shared<int>(i) en lugar de PtrT(new int(I)) asigna el recuento de referencia y el objeto juntos en la memoria en lugar de en lugares separados. En mis pruebas, esto mejoró el rendimiento de la desreferencia del puntero compartido en aproximadamente un 10-20%. Código para el que está por debajo:

#include <iostream> 
#include <boost/timer.hpp> 
#include <boost/shared_ptr.hpp> 
#include <boost/make_shared.hpp> 
#define SIZE 1000000 

typedef boost::shared_ptr<int> PtrT; 

int do_deref(PtrT* array) 
{ 
    int result = 0; 
    for(int j = 0; j != 1000; ++j) 
    { 
     for(int i = 0; i != SIZE; ++i) 
      result += *array[i]; 
    } 

    return result; 
} 

int main(void) 
{ 
    PtrT* array = new PtrT[SIZE]; 
    for(int i = 0; i != SIZE; ++i) 
     array[i] = boost::make_shared<int>(i); 
    boost::timer timer; 
    int result = do_deref(array); 
    std::cout << "deref took " << timer.elapsed() << "s" << std::endl; 
    return result; 
} 

Mis resultados (x86-64 Unbuntu 11 VM):

Original Raw: 6.93 
New Raw: 12.9 
Original Shared: 12.7 
New Shared: 10.59 
+4

"_Shared Pointers incluye un recuento de referencia, _" en realidad un puntero a una estructura de datos con 2 recuentos de ref, un puntero, un eliminador y tal vez incluso un mutex (con suerte no). – curiousguy

+3

@curious El tamaño es de 2 punteros, uno para la carga real, uno para la sobrecarga. Pero nada de eso afecta el costo de una simple disuasión. –

+0

¡Ah, muchas gracias! Adaptaré mi prueba para hacer que la comparación sea más justa. –

Cuestiones relacionadas