multithreading haskell concurrency node.js

multithreading - ¿Cuál es la respuesta de Haskell a Node.js?



concurrency (7)

¿Haskell puede proporcionar algunos de los beneficios de Node.js, a saber, una solución limpia para evitar el bloqueo de E / S sin tener que recurrir a la programación de subprocesos múltiples?

Sí, de hecho los eventos y los hilos están unificados en Haskell.

  • Puede programar en subprocesos ligeros explícitos (por ejemplo, millones de subprocesos en una sola computadora portátil).
  • O; puede programar en un estilo basado en eventos asíncronos, basado en la notificación de eventos escalable.

Los subprocesos en realidad se implementan en términos de eventos y se ejecutan en múltiples núcleos, con una migración de subprocesos perfecta, con un rendimiento documentado y aplicaciones.

Por ejemplo para

Colecciones simultáneas nbody en 32 cores

En Haskell tienes eventos e hilos, y como son todos los eventos bajo el capó.

Lea el documento que describe la implementación.

Creo que la comunidad de Erlang no tiene envidia de Node.js, ya que hace I / O sin bloqueo de forma nativa y tiene formas de escalar implementaciones fácilmente a más de un procesador (algo que ni siquiera está integrado en Node.js). Más detalles en http://journal.dedasys.com/2010/04/29/erlang-vs-node-js y Node.js o Erlang

¿Qué hay de Haskell? ¿Haskell puede proporcionar algunos de los beneficios de Node.js, a saber, una solución limpia para evitar el bloqueo de E / S sin tener que recurrir a la programación de subprocesos múltiples?

Hay muchas cosas que son atractivas con Node.js

  1. Eventos: no hay manipulación de subprocesos, el programador solo proporciona devoluciones de llamada (como en el marco Snap)
  2. Se garantiza que las devoluciones de llamada se ejecutarán en un solo hilo: no es posible ninguna condición de carrera.
  3. API agradable y simple para UNIX. Bonus: Excelente soporte HTTP. DNS también disponible.
  4. Cada E / S es asincrónica por defecto. Esto hace que sea más fácil evitar bloqueos. Sin embargo, demasiado procesamiento de la CPU en una devolución de llamada tendrá un impacto en otras conexiones (en este caso, la tarea se dividirá en subtareas más pequeñas y se volverá a programar).
  5. Mismo idioma para el lado del cliente y el lado del servidor. (Sin embargo, no veo demasiado valor en este caso. JQuery y Node.js comparten el modelo de programación de eventos, pero el resto es muy diferente. Simplemente no puedo ver cómo compartir código entre el lado del servidor y el lado del cliente podría ser útil en la práctica.)
  6. Todo esto empaquetado en un solo producto.


En primer lugar, no creo que node.js esté haciendo lo correcto al exponer todas esas devoluciones de llamada. Terminas escribiendo tu programa en CPS (estilo de paso de continuación) y creo que debería ser el trabajo del compilador hacer esa transformación.

Eventos: no hay manipulación de subprocesos, el programador solo proporciona devoluciones de llamada (como en el marco Snap)

Así que con esto en mente, puede escribir usando un estilo asíncrono si así lo desea, pero al hacerlo se perdería la escritura en un estilo síncrono eficiente, con un hilo por solicitud. Haskell es ridículamente eficiente en código síncrono, especialmente en comparación con otros idiomas. Es todos los eventos debajo.

Se garantiza que las devoluciones de llamada se ejecutarán en un solo hilo: no es posible ninguna condición de carrera.

Aún podría tener una condición de carrera en node.js, pero es más difícil.

Cada solicitud está en su propio hilo. Cuando escribes código que tiene que comunicarse con otros subprocesos, es muy simple hacerlo seguro para subprocesos gracias a los primitivos de concurrencia de haskell.

API agradable y simple para UNIX. Bonus: Excelente soporte HTTP. DNS también disponible.

Echa un vistazo en hackage y ver por ti mismo.

Cada E / S es asíncrona por defecto (aunque a veces esto puede ser molesto). Esto hace que sea más fácil evitar bloqueos. Sin embargo, demasiado procesamiento de la CPU en una devolución de llamada tendrá un impacto en otras conexiones (en este caso, la tarea se dividirá en subtareas más pequeñas y se volverá a programar).

No tiene tales problemas, ghc distribuirá su trabajo entre hilos reales del sistema operativo.

Mismo idioma para el lado del cliente y el lado del servidor. (Sin embargo, no veo demasiado valor en este caso. JQuery y Node.js comparten el modelo de programación de eventos, pero el resto es muy diferente. Simplemente no puedo ver cómo compartir código entre el lado del servidor y el lado del cliente podría ser útil en la práctica.)

Haskell no puede ganar aquí ... ¿verdad? Piensa de nuevo, http://www.haskell.org/haskellwiki/Haskell_in_web_browser .

Todo esto empaquetado en un solo producto.

Descargar ghc, fire up cabal. Hay un paquete para cada necesidad.


La pregunta es bastante ridícula porque 1) Haskell ya ha resuelto este problema de una manera mucho mejor y 2) de la misma manera que Erlang lo ha hecho. Aquí está el punto de referencia contra el nodo: yesodweb.com/blog/2011/03/…

Dé Haskell 4 núcleos y puede hacer 100k (simples) solicitudes por segundo en una sola aplicación. Nodo no puede hacer tantas, y no puede escalar una sola aplicación a través de los núcleos. Y no tiene que hacer nada para cosechar esto porque el tiempo de ejecución de Haskell no está bloqueado. El único otro lenguaje (relativamente común) que tiene IO no bloqueante incorporado en el tiempo de ejecución es Erlang.


Los eventos IMHO son buenos, pero la programación por medio de devoluciones de llamada no lo es.

La mayoría de los problemas que hacen especial la codificación y depuración de las aplicaciones web provienen de lo que las hace escalables y flexibles. Lo más importante, la naturaleza sin estado de HTTP. Esto mejora la navegabilidad, pero impone una inversión de control donde el elemento IO (el servidor web en este caso) llama a diferentes manejadores en el código de la aplicación. Este modelo de evento, o modelo de devolución de llamada, dicho con mayor precisión, es una pesadilla, ya que las devoluciones de llamada no comparten ámbitos variables, y se pierde una vista intuitiva de la navegación. Es muy difícil evitar todos los cambios de estado posibles cuando el usuario navega de un lado a otro, entre otros problemas.

Se puede decir que los problemas son similares a la programación de GUI donde el modelo de evento funciona bien, pero las GUI no tienen navegación ni botón de retroceso. Eso multiplica las transiciones de estado posibles en aplicaciones web. El resultado del intento de resolver estos problemas son marcos pesados ​​con configuraciones complicadas con muchos identificadores mágicos omnipresentes sin cuestionar la raíz del problema: el modelo de devolución de llamada y su inherente falta de intercambio de ámbitos variables, y ninguna secuencia, por lo que la secuencia debe Se construirá enlazando identificadores.

Existen marcos basados ​​en secuencias como ocsigen (ocaml) seaside (smalltalk) WASH (descontinuado, Haskell) y mflow (Haskell) que resuelven el problema de la administración del estado al tiempo que mantienen la navegabilidad y la REST. dentro de estos marcos, el programador puede expresar la navegación como una secuencia imperativa donde el programa envía las páginas y espera las respuestas en un solo hilo, las variables están dentro del alcance y el botón Atrás funciona automáticamente. Esto produce inherentemente un código más corto, más seguro y más legible donde la navegación es claramente visible para el programador. (Advertencia: soy el desarrollador de mflow)


Ok, así que habiendo visto un poco de la presentación de node.js que @gawi me señaló, puedo decir un poco más acerca de cómo Haskell se compara con node.js. En la presentación, Ryan describe algunos de los beneficios de Green Threads, pero luego continúa diciendo que no encuentra la falta de abstracción de un hilo como una desventaja. No estoy de acuerdo con su posición, particularmente en el contexto de Haskell: creo que las abstracciones que proporcionan los hilos son esenciales para hacer que el código del servidor sea más fácil de hacer, y más robusto. En particular:

  • el uso de un hilo por conexión le permite escribir un código que expresa la comunicación con un solo cliente, en lugar de escribir un código que se ocupe de todos los clientes al mismo tiempo. Piénselo así: un servidor que maneja múltiples clientes con subprocesos parece casi el mismo que uno que maneja un solo cliente; La principal diferencia es que hay un fork en algún lugar de la primera. Si el protocolo que está implementando es complejo, la administración de la máquina de estado para varios clientes se vuelve bastante complicada al mismo tiempo, mientras que los subprocesos le permiten simplemente escribir la comunicación con un solo cliente. El código es más fácil de entender, y más fácil de entender y mantener.

  • las devoluciones de llamada en un único subproceso del sistema operativo son multitarea cooperativa, a diferencia de la multitarea preventiva, que es lo que se obtiene con los subprocesos. La principal desventaja de la multitarea cooperativa es que el programador es responsable de asegurarse de que no haya hambre. Pierde modularidad: cometa un error en un lugar y puede arruinar todo el sistema. Esto es realmente algo de lo que no debe preocuparse, y la prevención es la solución simple. Por otra parte, la comunicación entre devoluciones de llamada no es posible (sería un punto muerto).

  • la concurrencia no es difícil en Haskell, porque la mayoría del código es puro y también es seguro para subprocesos en la construcción. Hay simples primitivas de comunicación. Es mucho más difícil dispararse en el pie con concurrencia en Haskell que en un lenguaje con efectos secundarios no restringidos.


Personalmente veo Node.js y la programación con devoluciones de llamada como algo innecesariamente de bajo nivel y poco natural. ¿Por qué programar con devoluciones de llamada cuando un buen tiempo de ejecución como el que se encuentra en GHC puede manejar las devoluciones de llamada por usted y hacerlo de manera bastante eficiente?

Mientras tanto, el tiempo de ejecución de GHC ha mejorado mucho: ahora cuenta con un "nuevo nuevo administrador de IO" llamado MIO donde creo que "M" significa multinúcleo. Se basa en el administrador de IO existente y su objetivo principal es superar la causa de la degradación del rendimiento de más de 4 núcleos. Los números de rendimiento proporcionados en este documento son bastante impresionantes. Verse a sí mismo:

Con Mio, los servidores HTTP realistas en Haskell escalan a 20 núcleos de CPU, logrando un rendimiento máximo de hasta 6,5 ​​veces en comparación con los mismos servidores que utilizan versiones anteriores de GHC. La latencia de los servidores Haskell también se ha mejorado: [...] bajo una carga moderada, reduce el tiempo de respuesta esperado en 5.7x en comparación con versiones anteriores de GHC

Y:

También mostramos que con Mio, McNettle (un controlador SDN escrito en Haskell) puede escalar efectivamente a más de 40 núcleos, alcanzar un rendimiento total de más de 20 millones de solicitudes por segundo en una sola máquina y, por lo tanto, convertirse en el más rápido de todos los controladores SDN existentes. .

Mio lo ha hecho en la versión GHC 7.8.1. Personalmente, veo esto como un gran paso adelante en el rendimiento de Haskell. Sería muy interesante comparar el rendimiento de las aplicaciones web existentes compilado por la versión anterior de GHC y 7.8.1.