¿Por qué movería std:: un rpp std:: compartido?


He estado mirando a través del código fuente Clang y encontré este fragmento:

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  Invocation = std::move(Value);
}

¿Por qué querría std::move un std::shared_ptr?

¿Hay algún punto para transferir la propiedad de un recurso compartido?

¿Por qué no haría esto en su lugar?

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  Invocation = Value;
}
Author: David Haim, 2017-01-26

5 answers

Creo que lo único que las otras respuestas no enfatizaron lo suficiente es el punto de velocidad.

std::shared_ptr el recuento de referencia es atómico. aumentar o disminuir el recuento de referencia requiere atómico incremento o decremento. Esto es cien veces más lento que incremento/decremento no atómico, sin mencionar que si incrementamos y decrementamos el mismo contador terminamos con el número exacto, perdiendo una tonelada de tiempo y recursos en proceso.

Moviendo el shared_ptr en lugar de copiarlo, le "roba" el atómica número de referencia y nos anulan el otro shared_ptr. "robar" el recuento de referencia no es atómico, y es cien veces más rápido que copiar el shared_ptr (y causar atómico incremento o decremento de referencia).

Tenga en cuenta que esta técnica se utiliza exclusivamente para la optimización. copiarlo (como sugeriste) es igual de bueno en cuanto a funcionalidad.

 71
Author: David Haim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-10 15:29:13

Al usar move se evita aumentar, y luego disminuir inmediatamente, el número de acciones. Eso podría ahorrarle algunas costosas operaciones atómicas en la cuenta de uso.

 99
Author: Bo Persson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-26 10:14:30

Las operaciones Move (como move constructor) para std::shared_ptr son baratas, ya que básicamente son "punteros de robo" (de fuente a destino; para ser más precisos, todo el bloque de control de estado es "robado" de fuente a destino, incluida la información de recuento de referencia).

En su lugar copiar operaciones en std::shared_ptr invocar atómico aumento de recuento de referencia (es decir, no solo ++RefCount en un entero RefCount miembro de datos, pero por ejemplo, llamando InterlockedIncrement en Windows), que es más caro que simplemente robar punteros/estado.

Así que, analizando la dinámica de recuento de ref de este caso en detalles:

// shared_ptr<CompilerInvocation> sp;
compilerInstance.setInvocation(sp);

Si pasas sp por valor y luego tomas una copia dentro del método CompilerInstance::setInvocation, tienes:

  1. Al ingresar el método, el parámetro shared_ptr se construye en copia: ref count atomic incremento .
  2. Dentro del cuerpo del método, copia el parámetro shared_ptr en el miembro de datos: ref count atomic incremento .
  3. Al salir del método, el parámetro shared_ptr se destruye: ref count atómico decremento .

Tienes dos incrementos atómicos y un decremento atómico, para un total de tres operaciones atómicas.

En su lugar, si pasa el parámetro shared_ptr por valor y luego std::move dentro del método (como se hace correctamente en el código de Clang), tienen:

  1. Al ingresar el método, el parámetro shared_ptr se construye en copia: ref count atomic incremento .
  2. Dentro del cuerpo del método, std::move el parámetro shared_ptr en el miembro de datos: ref count does not change! Solo estás robando punteros / estado: no hay operaciones costosas de conteo de ref atómicos involucradas.
  3. Al salir del método, el parámetro shared_ptr se destruye; pero como se movió en el paso 2, no hay nada que destruct, ya que el parámetro shared_ptr ya no apunta a nada. Una vez más, ningún decremento atómico ocurre en este caso.

En pocas palabras: en este caso se obtiene solo un incremento atómico de recuento de ref, es decir, solo una operación atómica.
Como puedes ver, esto es mucho mejorque dosincrementos atómicos más undecremento atómico (para un total de tres operaciones atómicas) para el caso de copia.

 54
Author: Mr.C64,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-26 14:46:50

Copiar un shared_ptr implica copiar su puntero de objeto de estado interno y cambiar el recuento de referencias. Moverlo solo implica intercambiar punteros al contador de referencia interno y al objeto de propiedad, por lo que es más rápido.

 18
Author: SingerOfTheFall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-26 21:02:56

Hay dos razones para usar std::move en esta situación. La mayoría de las respuestas abordaron la cuestión de la velocidad, pero ignoraron la importante cuestión de mostrar la intención del código más claramente.

Para un std::shared_ptr, std::move denota inequívocamente una transferencia de propiedad del puntero, mientras que una simple operación de copia agrega un propietario adicional. Por supuesto, si el propietario original posteriormente renuncia a su propiedad (por ejemplo, permitiendo que su std::shared_ptr sea destruido), entonces un se ha logrado la transferencia de propiedad.

Cuando transfieres la propiedad con std::move, es obvio lo que está sucediendo. Si utiliza una copia normal, no es obvio que la operación prevista es una transferencia hasta que verifique que el propietario original renuncia inmediatamente a la propiedad. Como ventaja adicional, es posible una implementación más eficiente, ya que una transferencia atómica de propiedad puede evitar el estado temporal donde el número de propietarios ha aumentado en uno (y los cambios concomitantes en recuentos de referencia).

 13
Author: Stephen C. Steel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-26 18:53:52