Rendimiento de estructuras de datos inmutables


No entiendo cómo puede algo como un Conjunto ser inmutable y todavía tener un rendimiento aceptable.

Por lo que he leído en F# Los conjuntos internamente usan Árboles Rojos Negros como su implementación. Si cada vez que queremos agregar algo nuevo a un Árbol Rojo Negro tenemos que recrearlo básicamente, ¿cómo puede tener un buen rendimiento? ¿Qué me estoy perdiendo aquí?

Aunque estoy pidiendo esto para los Conjuntos de F#, creo que esto es tan relevante en cualquier otro lenguaje que tenga o use datos inmutables estructura.

Gracias

Author: devoured elysium, 2010-07-13

8 answers

Casi todas las colecciones inmutables son alguna forma de árbol equilibrado. Para crear un nuevo árbol, debe reasignar nodos en la ruta del cambio (insertar, eliminar, "actualizar") a la raíz. Mientras el árbol esté equilibrado esto toma tiempo logarítmico. Si tienes algo como un árbol 2-3-4 (similar a los árboles rojo-negro) con un outdegree esperado tres, puedes manejar un millón de elementos usando solo 10 asignaciones.

Y en lenguajes donde se espera que las estructuras de datos sean puras, asegúrese de que la asignación sea rápida. Asignar un nodo de cuatro elementos va a costar una comparación, un incremento y cuatro almacenes. Y en muchos casos se puede amortizar el costo de una comparación sobre varias asignaciones.

Si desea saber más sobre cómo funcionan estas estructuras, una excelente fuente es Estructuras De Datos Puramente Funcionales por Chris Okasaki.

 36
Author: Norman Ramsey,
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
2010-07-13 02:27:27

No tienes que recrear todo el árbol. Muchas de las ramas permanecerán iguales y pueden ser "reutilizadas". Como un ejemplo simple, si el nuevo nodo necesita ser agregado a una hoja en el árbol actual, entonces solo los padres de ese nodo necesitan ser clonados y dar nuevas ramas.

 19
Author: jyoung,
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
2010-07-13 01:28:48

Como otros señalaron, no tienes que volver a crear toda la estructura de datos. Solo tienes que recrear las partes que han cambiado y hacer referencia a los subárboles existentes que permanecieron igual. Gracias a la inmutabilidad de la estructura de datos, puede reutilizar subárboles, por lo que casi nunca se necesita copiar todo. De hecho, si necesita clonar una estructura de datos mutable rara vez, podría tener un impacto mucho mayor.

En particular, para los árboles balanceados (como los árboles rojo-negro) esto da usted:

  • O (log N) tiempo de agregar / eliminar elementos del conjunto (igual que la implementación mutable)
  • O (log N) espacio (nuevas asignaciones) al agregar / eliminar elementos(mutable tendría O (1))

Esto puede ser, por supuesto, demasiada sobrecarga para algunas aplicaciones, pero en realidad no es tan malo. Además, la asignación en. NET garbage collector es muy rápida (creo, esencialmente O(1)), así que esto no es realmente un problema. Más asignación significa que GC necesita ejecutarse con más frecuencia, pero esto tampoco es tan crítico como puede sonar - las computadoras tienen bastante memoria en estos días. El. NET 4.0 realmente ayuda en muchos casos (ver también la respuesta de Jon Harrop aquí)

 12
Author: Tomas Petricek,
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-05-23 11:53:03

Como otros han dicho, una estructura de datos inmutable no tiene que ser completamente recreada, ya que puede reutilizar partes antiguas de sí misma. Puede hacer esto porque las partes antiguas son inmutables y se garantiza que los datos no cambiarán.

Tengo un ejemplo del mundo real de rendimiento inmutable. Hice algunas pruebas con un árbol inmutable Rojo Negro que hice en F# y solo funciona 3 veces más lento que std::sort en c++. Que creo que es muy rápido teniendo en cuenta que no fue diseñado específicamente para clasificar.

 10
Author: gradbot,
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-05-23 12:24:30

Las limitaciones de la semántica del lenguaje solo se aplican al código fuente en el lenguaje. La implementación(compilador, intérprete, entorno de tiempo de ejecución, etc.) es libre de hacer lo que quiera para el mejor rendimiento, siempre y cuando mantenga el mismo comportamiento. Esto es cierto para la mayoría de los idiomas.

Editar:

Se pueden hacer varias optimizaciones incluyendo el intercambio de datos (precisamente porque los datos son inmutables), usando mutabilidad entre bastidores, optimizando llamadas de cola (ya que FP usa un mucha recursión), y otros.

 4
Author: Cogwheel,
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
2010-07-13 01:14:53

Véase

Programación funcional: eficiencia de la estructura de datos inmutable

(especialmente mi respuesta que apunta a la charla de Rich Hickey) por la evidencia convincente 'general' de que sí, las estructuras inmutables también pueden ser muy eficientes.

En cuanto a lo bien que esto es cierto en el caso específico de F# Set, bueno, tal vez solo moderadamente hoy. Sería genial utilizar una estructura subyacente más eficiente (en términos pragmáticos; en términos teóricos, por supuesto, todo is O (logN) (que en términos prácticos es O(1))).

 3
Author: Brian,
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-05-23 12:30:21

Simplemente un Conjunto es una entidad de almacenamiento basada en nodos. En el caso de un Conjunto, puede implementarlo como un árbol en el que no está recreando todos los bordes y los nodos cuando "agrega" un elemento a la siguiente versión del Conjunto, sino que solo está creando un nuevo conjunto de bordes. Puede hacer esto porque los nodos en sí nunca cambiarán, ni tampoco los objetos que se mantienen dentro de ellos.

El beneficio real se encuentra dentro de aplicaciones de un solo subproceso, sino más bien en aplicaciones multiproceso. Las estructuras de datos inmutables eliminan la necesidad de mecanismos de bloqueo. Si nunca van a cambiar, no tienes que preocuparte por el estado.

 2
Author: wheaties,
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
2010-07-13 01:33:37

No estoy seguro de cómo se implementa esto en el lenguaje, pero las estructuras de datos podrían percibirse como inmutables para el programador, pero optimizadas entre bastidores.

Por ejemplo, tengo una lista a=[1,2,3,4,5]. I append 6. b=[a [6]] y ambos pueden ser inmutables. No se pierde ningún rendimiento al hacer esto, y es más rápido que copiar los valores.

Entonces, déjame preguntarte, porque no lo sé, ¿por qué sería más lento hacer las cosas como inmutables? En el caso del árbol, yo veo tu punto. Supongo que tendría que recrear nodos por encima del nodo actual, pero no por debajo (suponiendo que tengamos punteros hijos y no punteros padres).

 2
Author: gtrak,
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
2010-07-13 01:39:32