9

Uno de los puntos de venta de las estructuras de datos inmutables es que son paralelizables automáticamente. Si no se produce ninguna mutación, las referencias a una estructura de datos funcional se pueden pasar entre hilos sin ningún bloqueo.¿Las estructuras de datos funcionales/inmutables aún pueden ser útiles para la concurrencia en un contexto no relacionado con la basura?

Comencé a pensar cómo se implementarían las estructuras de datos funcionales en C++. Supongamos que tenemos un recuento de referencias en cada nodo de nuestra estructura de datos. (Las estructuras de datos funcionales comparten estructura entre miembros antiguos y actualizados de una estructura de datos, por lo que los nodos no pertenecen exclusivamente a una estructura de datos particular.)

El problema es que si los recuentos de referencia se actualizan en diferentes subprocesos, entonces nuestros datos la estructura ya no es segura para subprocesos. Y asociar un mutex a cada nodo es costoso y frustra el propósito de utilizar estructuras de datos inmutables para la concurrencia.

¿Hay alguna manera de hacer que las estructuras de datos inmutables simultáneas funcionen en C++ (y otros entornos no recolectados)?

+0

No soy experto en esto, pero puede almacenar recuentos de referencia por subproceso, y solo colocar un bloqueo en ellos para comprobar si todos los subprocesos han llegado a cero de referencia en un nodo. Pero estoy seguro de que hay soluciones más elegantes que esto o quizás el recuento de referencias en general. – sinelaw

+0

Aquí hay una popular estructura de datos inmutables para C++ http://www.sgi.com/tech/stl/Rope.html – ybungalobill

+0

Puede ver las implementaciones de 'shared_ptr', que enfrenta exactamente el mismo problema con la necesidad de un hilo eficiente- cuenta de referencia segura. –

Respuesta

5

Hay algoritmos de conteo de referencia sin enclavamiento, véase, p. Lock-Free Reference Counting, Atomic Reference Counting Pointers.

También tenga en cuenta que C++ 0x (que probablemente se convierta pronto en C++ 11) contiene tipos enteros atómicos especialmente para fines como este.

+0

No estoy seguro de estar aquí, pero no usar el recuento de referencias degradaría el rendimiento de O (log n) a O (n), porque tendrías que actualizar las recuentos de todos los nodos de tu "descendencia" recién creada de la colección reutiliza? Esto derrota el propósito de una colección inmutable para la mayoría de los casos de uso, supongo. ¡Pero por favor corrígeme si estoy equivocado! –

+0

@le_me No tengo idea de qué estructura de datos Rob estaba tratando de tener, así que no puedo decir nada sobre sus características de tiempo. Podría ser O (log n), podría ser O (n^3), podría ser O (exp (n)) o cualquier otra cosa.El efecto del recuento de referencias requerirá un segundo análisis, según el patrón de uso. Además, tener recuentos de referencia fue su idea, no la mía. Es posible que haya respondido a la parte equivocada de un problema XY, por supuesto, pero aún creo que su comentario estaría mejor ubicado en la pregunta, donde se sugirió el recuento de referencias. –

+0

Solo quería agregar información a la solución específica que presentó. Creo que es importante tener en cuenta que el recuento de referencias tiene una penalización de rendimiento con una complejidad de tiempo posiblemente mayor que la colección en sí. P.ej. [Las colecciones inmutables de Microsofts parecen tener complejidad O (log n)] (https://blogs.msdn.microsoft.com/bclteam/2012/12/18/preview-of-immutable-collections-released-on-nuget/) o menos para cualquier operación. Entonces, en ese caso, el recuento de referencias tendría un impacto severo en el rendimiento para grandes conjuntos de datos. –

4

Bueno, los lenguajes recogidos de basura también tienen el problema de los entornos con múltiples subprocesos (y no es fácil para estructuras mutables).

Ha olvidado que, a diferencia de los datos arbitrarios, los contadores se pueden incrementar y disminuir atómicamente, por lo que no sería necesario un mutex. Todavía significa que se debe mantener sincronizada la memoria caché entre los procesadores, lo que puede costar mucho dinero si se actualiza un solo nodo.

+2

Mira los contadores descuidados (http://www.usenix.org/events/osdi10/tech/full_papers/Boyd-Wickizer.pdf) como una forma de implementar contadores de referencia que no causan tanta contención de caché. El documento mencionado también menciona varios otros enfoques para mirar. – Karmastan

+0

@Karmastan: gracias por el artículo! No estoy seguro de si funcionaría, según el artículo "Esta operación es costosa, por lo que los contadores descuidados solo deberían usarse para objetos que se asignan con relativa poca frecuencia", pero la idea en sí misma es interesante. Otra técnica sobre la que había leído es mantener los contadores separados de los objetos, y tener un solo hilo actualizándolos, con colas TLS para almacenar las operaciones inc/dec. –

Cuestiones relacionadas