¿Por qué son tan populares los punteros inteligentes de conteo de referencias?

50

Como puedo ver, los punteros inteligentes se utilizan ampliamente en muchos proyectos de C ++ del mundo real.

Aunque algunos tipos de punteros inteligentes son obviamente beneficiosos para admitir RAII y las transferencias de propiedad, también hay una tendencia de usar punteros compartidos por defecto , como una forma de "recolección de basura" , para que el programador no tenga que pensar mucho en la asignación.

¿Por qué los punteros compartidos son más populares que la integración de un recolector de basura adecuado como Boehm GC ? (¿O acuerdas en absoluto que son más populares que los GC reales)?

Sé de dos ventajas de los GC convencionales sobre el recuento de referencias:

  • Los algoritmos GC convencionales no tienen problemas con los ciclos de referencia .
  • El recuento de referencias es generalmente más lento que un GC adecuado.

¿Cuáles son las razones para usar punteros inteligentes de recuento de referencias?

    
pregunta Miklós Homolya 14.08.2013 - 04:28

5 respuestas

56

Algunas ventajas del recuento de referencias sobre la recolección de basura:

  1. Baja sobrecarga. Los recolectores de basura pueden ser bastante intrusivos (por ejemplo, congelar su programa en momentos impredecibles mientras se procesa un ciclo de recolección de basura) y consumir mucha memoria (por ejemplo, la huella de memoria de su proceso aumenta innecesariamente a muchos megabytes antes de que la recolección de basura finalmente se active)

  2. Comportamiento más predecible. Con el conteo de referencias, se le garantiza que su objeto será liberado en el instante en que la última referencia desaparezca. Por otro lado, con la recolección de basura, su objeto se liberará "en algún momento", cuando el sistema lo consiga. Para la RAM, esto no suele ser un gran problema en los escritorios o servidores con poca carga, pero para otros recursos (por ejemplo, los manejadores de archivos), a menudo es necesario cerrarlos lo antes posible para evitar posibles conflictos más adelante.

  3. Más simple. El conteo de referencias se puede explicar en unos pocos minutos y se puede implementar en una o dos horas. Los recolectores de basura, especialmente aquellos con un rendimiento decente, son extremadamente complejos y no hay mucha gente que los entienda.

  4. Estándar. C ++ incluye el recuento de referencias (a través de shared_ptr) y amigos en el STL, lo que significa que la mayoría de los programadores de C ++ están familiarizados con él y la mayoría de los códigos de C ++ funcionarán con él. Sin embargo, no hay ningún recolector de basura estándar de C ++, lo que significa que tiene que elegir uno y esperar que funcione bien para su caso de uso, y si no lo hace, es su problema solucionarlo, no del idioma.

En cuanto a las supuestas desventajas del conteo de referencias, no detectar los ciclos es un problema, pero uno en el que nunca me he encontrado personalmente en los últimos diez años de uso del conteo de referencias. La mayoría de las estructuras de datos son naturalmente acíclicas, y si se encuentra con una situación en la que necesita referencias cíclicas (por ejemplo, un puntero principal en un nodo de árbol), puede usar un valor débil_ptr o un puntero C en bruto para la "dirección hacia atrás". Siempre que esté al tanto del problema potencial cuando está diseñando sus estructuras de datos, no hay problema.

En cuanto al rendimiento, nunca he tenido un problema con el rendimiento del recuento de referencias. He tenido problemas con el rendimiento de la recolección de basura, en particular los congelamientos aleatorios en los que puede incurrir GC, a los que la única solución ("no asignar objetos") también se podría reformular como "no usar GC" .

    
respondido por el Jeremy Friesner 14.08.2013 - 04:48
26

Para obtener un buen rendimiento de un GC, el GC debe poder mover objetos en la memoria. En un lenguaje como C ++ donde puedes interactuar directamente con las ubicaciones de la memoria, esto es prácticamente imposible. (Microsoft C ++ / CLR no cuenta porque introduce una nueva sintaxis para los punteros administrados por GC y, por lo tanto, es un lenguaje diferente).

El Boehm GC, aunque es una idea ingeniosa, es en realidad el peor de los dos mundos: necesita un malloc () más lento que un buen GC, por lo que pierde el comportamiento determinista de asignación / desasignación sin el aumento de rendimiento correspondiente de un CG generacional. Además, es necesariamente conservador, por lo que no necesariamente recogerá toda su basura de todos modos.

Un GC bueno y bien afinado puede ser una gran cosa. Pero en un lenguaje como C ++, las ganancias son mínimas y los costos a menudo simplemente no valen la pena.

Sin embargo, será interesante ver cómo C ++ 11 se vuelve más popular, ya sea que las lambdas y la semántica de captura empiecen a guiar a la comunidad de C ++ hacia los mismos tipos de problemas de asignación y vida útil del objeto que hicieron que la comunidad Lisp inventara los GCs. en primer lugar.

Consulte también mi respuesta a una pregunta relacionada sobre StackOverflow .

    
respondido por el Daniel Pryden 14.08.2013 - 09:04
4
  

Como puedo ver, los punteros inteligentes se utilizan ampliamente en muchos proyectos de C ++ del mundo real.

Es cierto, pero, objetivamente, la gran mayoría de los códigos ahora están escritos en lenguajes modernos con rastreo de recolectores de basura.

  

Aunque algunos tipos de punteros inteligentes son obviamente beneficiosos para admitir RAII y las transferencias de propiedad, también hay una tendencia de usar punteros compartidos de forma predeterminada, como una forma de "recolección de basura", para que el programador no tenga que pensar Asignación que tanto.

Es una mala idea porque aún debes preocuparte por los ciclos.

  

¿Por qué los punteros compartidos son más populares que la integración de un recolector de basura adecuado como Boehm GC? (¿O acuerdas en absoluto que son más populares que los GC reales)?

Oh, wow, hay tantas cosas mal con tu línea de pensamiento:

  1. El GC de Boehm no es un GC "adecuado" en ningún sentido de la palabra. Es realmente horrible. Es conservador por lo que gotea y es ineficiente por diseño. Consulte: enlace

  2. Los punteros compartidos no son, objetivamente, tan populares como GC porque la gran mayoría de los desarrolladores están usando lenguajes GC'd ahora y no necesitan punteros compartidos. Simplemente observe Java y Javascript en el mercado laboral en comparación con C ++.

  3. Parece que estás restringiendo la consideración a C ++ porque, supongo, piensas que GC es un problema tangencial. No es (la forma única de obtener un GC decente es diseñar el lenguaje y la VM desde el principio), por lo que está introduciendo un sesgo de selección. Las personas que realmente quieren una recolección de basura adecuada no se quedan con C ++.

  

¿Cuáles son las razones para usar punteros inteligentes de recuento de referencias?

Está restringido a C ++, pero desea tener una administración de memoria automática.

    
respondido por el Jon Harrop 26.01.2016 - 22:10
3

En MacOS X y iOS, y con los desarrolladores que usan Objective-C o Swift, el conteo de referencias es popular porque se maneja automáticamente, y el uso de recolección de basura ha disminuido considerablemente desde que Apple ya no lo admite (me han dicho que las aplicaciones que utilizan la recolección de basura se interrumpirán en la próxima versión de MacOS X, y la recolección de basura nunca se implementó en iOS). De hecho, dudo seriamente que existiera mucho software que utilizara la recolección de basura cuando estaba disponible.

La razón para deshacerse de la recolección de basura: nunca funcionó de manera confiable en un entorno de estilo C donde los punteros podrían "escapar" a áreas a las que el recolector de basura no puede acceder. Apple cree firmemente y cree que el conteo de referencias es más rápido. (Puede hacer cualquier reclamación aquí sobre la velocidad relativa, pero nadie ha podido convencer a Apple). Y al final, nadie utilizó la recolección de basura.

Lo primero que aprende cualquier desarrollador de MacOS X o iOS es cómo manejar los ciclos de referencia, por lo que no es un problema para un desarrollador real.

    
respondido por el gnasher729 27.01.2016 - 01:53
2

La mayor desventaja de la recolección de basura en C ++ es que es simplemente imposible hacerlo bien:

  • En C ++, los punteros no viven en su propia comunidad amurallada, se mezclan con otros datos. Como tal, no puede distinguir un puntero de otros datos que, por casualidad, tienen un patrón de bits que puede interpretarse como un puntero válido.

    Consecuencia: cualquier recolector de basura de C ++ filtrará objetos que deberían ser recolectados.

  • En C ++, puedes hacer aritmética de punteros para derivar punteros. Como tal, si no encuentra un puntero al comienzo de un bloque, eso no significa que no se pueda hacer referencia a ese bloque.

    Consecuencia: cualquier recolector de basura de C ++ debe tener en cuenta estos ajustes, al tratar cualquier secuencia de bits que tenga lugar en cualquier punto del bloque, incluso justo después de su finalización, como un puntero válido que hace referencia al bloque.

    Nota: ningún recolector de basura de C ++ puede manejar código con trucos como estos:

    int* array = new int[7];
    array--;    //undefined behavior, but people may be tempted anyway...
    for(int i = 1; i <= 7; i++) array[i] = i;
    

    Es cierto, esto invoca un comportamiento indefinido. Pero algún código existente es más inteligente de lo que es bueno para él, y puede desencadenar una desasignación preliminar por parte de un recolector de basura.

respondido por el cmaster 26.01.2016 - 22:34

Lea otras preguntas en las etiquetas