2008-10-03 10 views
25

Actualmente estoy aprendiendo programación funcional en mi tiempo libre con Scala, y tengo una pregunta para principiantes ociosa.Objetos funcionales inmutables en el dominio altamente mutable

Veo la elegancia de tener objetos inmutables al hacer algo como calcular una transformada wavelet de Haar, es decir, cuando los datos que están siendo representados por los objetos no cambian.

Pero vi un blog en el que alguien tenía un pequeño juego como ejemplo cuando demostraba la inmutabilidad. Si un objeto de criatura recibió daño, no cambió su estado: devolvió un nuevo objeto de criatura con los nuevos puntos de golpe y una nueva bandera de "aggro hacia X". Pero si tuviéramos que diseñar algo así como un MMORPG, World of Warcraft dice. Cien jugadores en un campo de batalla ... posiblemente miles de ataques y efectos de hechizo debilitador/debilitador que los afectan de diferentes maneras. ¿Todavía es posible diseñar el sistema con objetos completamente inmutables? Para mí, parecería que habría un enjambre descomunal de nuevas instancias cada 'tic'. Y para obtener la instancia de objetos actualmente válida, todos los clientes tendrían que pasar constantemente por algún tipo de objeto central de "mundo del juego", o?

¿Funciona la escala de programación para esto, o se trata de una "mejor herramienta para el mejor trabajo, probablemente no inmutable aquí"?

+0

Podría publicar un enlace a esa entrada en el blog? –

Respuesta

16

A mí me parece que habrá un enjambre descomunal de nuevas instancias cada 'tic'.

De hecho, ese es el caso. Tengo una aplicación Haskell que lee un feed de datos del mercado (cerca de cinco millones de mensajes en el transcurso de un día de negociación de seis horas, para los datos que nos interesan) y mantiene el "estado actual" para varias cosas, como el los precios y cantidades más recientes de ofertas y ofertas para los instrumentos, qué tan bien encaja nuestro modelo en el mercado, etc. etc. Es bastante aterrador simular una ejecución de este programa frente a una alimentación grabada en el modo de perfilado y verlo asignar y GC cerca de 288 TB de memoria (o cerca de 50,000 veces el tamaño de la memoria RAM de mi máquina) en los primeros 500 segundos de su ejecución.(La cifra sería considerablemente más alta sin crear perfiles, ya que el perfilado no solo ralentiza la aplicación, sino que también obliga a ejecutarlo en un núcleo).

Pero tenga en cuenta que el recolector de basura en implementaciones de lenguaje puro está optimizado para este tipo de comportamiento. Estoy bastante satisfecho con la velocidad general de mi aplicación, y creo que es bastante exigente, ya que tenemos que analizar varios cientos de mensajes por segundo a partir del feed del mercado, hacer algunos cálculos bastante extensos para construir nuestro modelo, y usar eso modelo para generar pedidos para ir al intercambio lo más rápido posible.

0

Como casi todas las herramientas de programación, los objetos inmutables son potentes, pero peligrosos en la situación incorrecta. Creo que el ejemplo del juego no es muy bueno o al menos muy artificial.

Eric Lippert tiene algunos interesantes posts sobre el tema de la inmutabilidad, y son una lectura bastante interesante.

+3

No estoy de acuerdo: los objetos inmutables son * siempre * más seguros que los objetos mutables. Con los objetos mutables tienes que lidiar con la posibilidad de aliasing, y también tienes más dificultad para comprenderlos porque tienes que hacer un seguimiento de su estado actual al construir un modelo mental de tu programa. Los objetos inmutables resuelven estos problemas y no introducen otros nuevos. (Pueden o no ser menos eficientes, pero eso no es "peligroso"). El sitio web al que hizo referencia tiene muchos mensajes que señalan todas las cosas peligrosas que pueden suceder en un mundo mutable. –

4

Un MMORPG es ya un ejemplo de inmutabilidad. Dado que el juego se distribuye entre los servidores y los sistemas de los jugadores, no existe absolutamente ningún objeto central de "mundo del juego". Por lo tanto, cualquier objeto que se envíe a través del cable es inmutable — porque el receptor no lo cambia. En cambio, se envía un nuevo objeto o mensaje como respuesta, si es que hay uno.

Nunca he escrito un juego distribuido, así que no sé exactamente cómo se implementan, pero sospecho que las actualizaciones de los objetos se calculan localmente o se envían como diffs a través del cable.

Por ejemplo, está jugando Comando & Conquer. Tu mamut tank está sentado en el modo listo protegiendo tu base. Tu oponente se acerca con un tanque ligero para explorar tu base. Tu tanque de mamut dispara y golpea el tanque de tu oponente, causando daño.

Este juego es bastante simple, así que sospecho que se calcula mucho localmente siempre que sea posible. Supongamos que las computadoras de los dos jugadores están inicialmente sincronizadas en términos de estado del juego. Luego, tu oponente hace clic para mover su tanque ligero a tu base. Se le envía un mensaje (inmutable) por el cable. Dado que el algoritmo para mover un tanque es (probablemente) determinista, tu copia de Command & Conquer puede mover el tanque de tu oponente en la pantalla, actualizando el estado de tu juego (podría ser inmutable o mutable). Cuando el tanque ligero se encuentra dentro del alcance de tu enorme tanque, tu tanque se dispara. Se genera un valor aleatorio en el servidor (en este caso, una computadora se elige arbitrariamente como el servidor) para determinar si el disparo golpea a su oponente o no. Suponiendo que se golpeó el tanque y se debe actualizar el tanque de su oponente, solo se difunde el diff — el hecho de que el nuevo nivel de blindaje del tanque se ha reducido a 22% — a través del cable para sincronizar los juegos de los dos jugadores. Este mensaje es inmutable.

Si el objeto en la computadora de cualquiera de los jugadores es mutable o inmutable es irrelevante; se puede implementar de cualquier manera.Cada jugador no cambia directamente el estado del juego de otros jugadores.

3

Un punto a tener en cuenta sobre la inmutabilidad es que (si se implementa correctamente) hace que la creación de objetos sea relativamente liviana. Si un campo es inmutable, se puede compartir entre instancias.

3

Es importante tener en cuenta al diseñar un programa funcional que, como usted dice, los objetos inmutables tendrán algunos gastos generales. También es importante recordar que tener objetos en tu programa de MMORPG ser inmutable será intrínsecamente más escalable. Por lo tanto, la inversión inicial en equipos puede ser mayor, pero en el futuro, a medida que las cosas se expandan, podrás escalar a tu base de jugadores.

Otra cosa importante a considerar es que ahora mismo las máquinas más robustas tienen 6 núcleos por CPU. Considere una máquina de doble CPU con 6 núcleos cada uno. Uno de estos 12 núcleos puede estar haciendo recolección de basura y, por lo tanto, la sobrecarga de derribar muchos objetos se puede compensar con la aplicación que se puede ampliar fácilmente a esos otros 11 núcleos.

Recuerde también que no todos los objetos (y sus sub objetos) deben reconstruirse por completo en una copia. Cualquier tipo de referencia que no haya cambiado solo tomará una única asignación de referencia cuando se "copie" un objeto.

6

Normalmente en programación funcional no tendrá constructores de estilo C++. Entonces, aunque conceptualmente está creando objetos todo el tiempo, eso no significa que el compilador tenga que crear un código para asignar un nuevo objeto, porque no puede afectar el comportamiento del programa. Como los datos son inmutables, el compilador puede ver qué valores acaba de especificar y qué ha pasado a sus funciones.

Luego, el compilador puede crear código compilado muy ajustado que simplemente calcula los campos en los objetos específicos cuando son necesarios. Qué tan bien funcione esto depende de la calidad del compilador que use. Sin embargo, el código de programación funcional limpio le dice al compilador mucho más acerca de su código que lo que podría suponer un compilador de C para un programa similar, por lo que un buen compilador puede generar un código mejor de lo que cabría esperar.

Así que, al menos en teoría, no hay razón para preocuparse; las implementaciones de programación funcional pueden escalar al igual que las implementaciones de asignación de montón orientadas a objetos. En la práctica, debe comprender la calidad de la implementación del idioma con la que está trabajando.

3

No piense en la creación de objetos en el nivel del cable. Por ejemplo, un tiempo de ejecución optimizado para un lenguaje funcional probablemente podrá "engañar" cuando se trata de reemplazar un objeto y una mutación real de la estructura existente, si no sabe nada hará referencia al original y el nuevo lo reemplazará por completo. Piense en Optimización de la Recursión de la Cola, pero aplicado al estado del objeto.

Cuestiones relacionadas