¿Cuál es la penalización de rendimiento de débil_ptr?

¿Cuál es la penalización de rendimiento de débil_ptr?

Desde el código fuente de Boost 1.42 (<boost/shared_ptr/weak_ptr.hpp> línea 155):

shared_ptr<T> lock() const // never throws
{
    return shared_ptr<element_type>( *this, boost::detail::sp_nothrow_tag() );
}

ergo, el comentario de James McNellis es correcto; es el costo de copiar-construir un shared_ptr .


Para mi propio proyecto, pude mejorar drásticamente el rendimiento al agregar #define BOOST_DISABLE_THREADS antes de que se incluya cualquier refuerzo. Esto evita la sobrecarga de spinlock/mutex de débil_ptr::bloqueo que en mi proyecto era un gran cuello de botella. Como el proyecto no es wrt boost multiproceso, podría hacer esto.


Usar/desreferenciar un shared_ptr es casi como acceder a un ptr sin procesar, bloquear un débil_ptr es una operación "pesada" de rendimiento en comparación con el acceso de puntero normal, porque este código tiene que ser "consciente de subprocesos" para funcionar correctamente en caso de que otro subproceso active la liberación de el objeto al que hace referencia el puntero. Como mínimo, debe realizar algún tipo de operación interbloqueada/atómica que, por definición, es mucho más lenta que el acceso normal a la memoria.

Como de costumbre, una forma de ver lo que está pasando es inspeccionar el código generado:

#include <memory>

class Test
{
public:
    void test();
};

void callFuncShared(std::shared_ptr<Test>& ptr)
{
    if (ptr)
        ptr->test();
}

void callFuncWeak(std::weak_ptr<Test>& ptr)
{
    if (auto p = ptr.lock())
        p->test();
}

void callFuncRaw(Test* ptr)
{
    if (ptr)
        ptr->test();
}

El acceso a través de shared_ptr y el puntero sin formato es el mismo. Desde shared_ptr se pasó como referencia, necesitamos cargar el valor referenciado, por eso la diferencia es solo una carga adicional para la versión shared_ptr.

callFuncShared:

llamarFuncDébil:

Llamando a través de weak_ptr produce 10 veces más código y, en el mejor de los casos, tiene que pasar por un intercambio de comparación bloqueado, lo que por sí solo requerirá más de 10 veces el tiempo de CPU que desreferenciar raw o shared_ptr:

Solo si el contador compartido no es cero, solo entonces puede cargar el puntero al objeto real y usarlo (llamando al objeto o creando un shared_ptr ).