¿Cuáles son los inconvenientes de hacer una implementación de tiempo de ejecución de JavaScript multiproceso? [cerrado]


51

He estado trabajando en una implementación de tiempo de ejecución de JavaScript multiproceso durante la semana pasada. Tengo una prueba de concepto hecha en C ++ usando JavaScriptCore y boost.

La arquitectura es simple: cuando el tiempo de ejecución termina de evaluar el script principal, se inicia y se une a un grupo de subprocesos, que comienza a seleccionar tareas de una cola de prioridad compartida, si dos tareas intentan acceder a una variable al mismo tiempo, se marca atómica y compiten por el acceso .

Trabajo en tiempo de ejecución multiproceso node.js

El problema es que cuando muestro este diseño a un programador de JavaScript recibo comentarios extremadamente negativos, y no tengo idea de por qué. Incluso en privado, todos dicen que JavaScript está destinado a ser de un solo subproceso, que las bibliotecas existentes tendrían que reescribirse, y que los gremlins generarán y se comerán a todos los seres vivos si sigo trabajando en esto.

Originalmente, también tenía una implementación de rutina nativa (usando contextos de impulso), pero tuve que deshacerme de ella (JavaScriptCore es pedante sobre la pila), y no quería arriesgar su ira, así que decidí no mencionarlo.

¿Qué piensas? ¿JavaScript está destinado a ser de un solo subproceso, y debe dejarse solo? ¿Por qué todos están en contra de la idea de un tiempo de ejecución de JavaScript concurrente?

Editar: El proyecto ahora está en GitHub , experimente con usted mismo y hágame saber lo que piensa.

La siguiente es una imagen de las promesas que se ejecutan en todos los núcleos de CPU en paralelo sin contención:

Ejecutar promesas al mismo tiempo.


77
Esta parece una pregunta muy obstinada. ¿Le preguntaste a las personas que aparentemente no les gustó tu idea por qué creen que será problemático?
5gon12eder

26
Agregar hilos a algo que no está destinado a ser multiproceso es como convertir una carretera de un solo carril en una autopista sin proporcionar la educación del conductor. Funcionará bastante bien la mayor parte del tiempo, hasta que las personas comiencen a fallar al azar. Con el subprocesamiento múltiple, tendrá errores de sincronización sutiles que no puede reproducir o un comportamiento errático la mayor parte del tiempo. Tienes que diseñar con eso en mente. Necesita sincronización de subprocesos. Simplemente hacer variables atómicas no elimina las condiciones de carrera.
mgw854

2
¿Cómo planea manejar el acceso multiproceso al estado compartido? "Marcado como atómico y compite por el acceso" no explica cómo cree que esto realmente funcionaría. Supongo que las actitudes negativas hacia la idea se deben a que las personas no tienen idea de cómo hacer que esto funcione. O, si está poniendo toda la carga sobre el desarrollador, como en Java o C ++, para usar mutexes adecuados y tal, entonces la gente probablemente esté pensando por qué quieren esa complicación y riesgo de programación en un entorno libre de él.
jfriend00

17
Debido a que la coordinación automática del estado aleatorio entre hilos se considera un problema casi imposible, por lo que no tiene credibilidad de que pueda ofrecer algo que lo haga automáticamente. Y, si solo va a devolver la carga al desarrollador como lo hacen Java o C ++, entonces la mayoría de los programadores de node.js no quieren esa carga, les gusta ese node.js no tiene que lidiar con eso por la mayor parte Si desea un oído más comprensivo, tendrá que explicar / mostrar cómo y qué ofrecería a este respecto y por qué sería bueno y útil.
jfriend00

3
Por favor continúa tu trabajo. Considero idiomas sin subprocesos múltiples como idiomas de juguete. Creo que la mayoría de los desarrolladores de JavaScript trabajan con el navegador, que tiene un modelo de subproceso único.
Chloe

Respuestas:


70

1) El subprocesamiento múltiple es extremadamente difícil y, desafortunadamente, la forma en que has presentado esta idea hasta ahora implica que estás subestimando severamente lo difícil que es.

Por el momento, parece que simplemente está "agregando hilos" al idioma y preocupándose por cómo corregirlo y realizarlo más tarde. En particular:

Si dos tareas intentan acceder a una variable simultáneamente, se marca como atómica y se disputan el acceso.
...
Estoy de acuerdo en que las variables atómicas no resolverán todo, pero trabajar en una solución para el problema de sincronización es mi próximo objetivo.

Agregar hilos a Javascript sin una "solución para el problema de sincronización" sería como agregar enteros a Javascript sin una "solución para el problema de adición". Es tan fundamental para la naturaleza del problema que básicamente no tiene sentido ni siquiera discutir si vale la pena agregar múltiples subprocesos sin una solución específica en mente, sin importar cuán mal lo deseemos.

Además, hacer que todas las variables sean atómicas es el tipo de cosa que probablemente haga que un programa multiproceso funcione peor que su contraparte de un solo hilo, lo que hace que sea aún más importante probar el rendimiento en programas más realistas y ver si está ganando algo o no.

Tampoco me queda claro si está tratando de mantener los hilos ocultos del programador node.js o si planea exponerlos en algún momento, creando efectivamente un nuevo dialecto de Javascript para la programación multiproceso. Ambas opciones son potencialmente interesantes, pero parece que aún no has decidido a cuál estás apuntando.

Entonces, en este momento, le está pidiendo a los programadores que consideren cambiar de un entorno de subproceso único a un nuevo entorno de subprocesos múltiples que no tiene solución para el problema de sincronización y no hay evidencia de que mejore el rendimiento en el mundo real, y aparentemente no hay un plan para resolver esos problemas.

Probablemente por eso la gente no te toma en serio.

2) La simplicidad y robustez del bucle de evento único es una gran ventaja.

Los programadores de Javascript saben que el lenguaje Javascript está "a salvo" de las condiciones de carrera y otros errores extremadamente insidiosos que afectan a toda la programación genuinamente multiproceso. El hecho de que necesiten argumentos sólidos para convencerlos de que renuncien a la seguridad no los hace de mente cerrada, los hace responsables.

A menos que pueda mantener esa seguridad de alguna manera, cualquiera que desee cambiar a un nodo multiproceso .js probablemente sería mejor cambiar a un lenguaje como Go diseñado desde cero para aplicaciones multiproceso.

3) Javascript ya admite "subprocesos en segundo plano" (WebWorkers) y programación asincrónica sin exponer directamente la gestión de subprocesos al programador.

Esas características ya resuelven muchos de los casos de uso comunes que afectan a los programadores de Javascript en el mundo real, sin renunciar a la seguridad del bucle de eventos únicos.

¿Tiene en mente algunos casos de uso específicos que estas características no resuelven y para los que los programadores de Javascript quieren una solución? Si es así, sería una buena idea presentar su node.js multiproceso en el contexto de ese caso de uso específico.


PD ¿Qué me convencería de intentar cambiar a una implementación de node.js multiproceso?

Escriba un programa no trivial en Javascript / node.js que cree que se beneficiaría de un subprocesamiento múltiple genuino. Realice pruebas de rendimiento en este programa de muestra en el nodo normal y en su nodo multiproceso. Muéstreme que su versión mejora el rendimiento del tiempo de ejecución, la capacidad de respuesta y el uso de múltiples núcleos en un grado significativo, sin introducir errores ni inestabilidad.

Una vez que hayas hecho eso, creo que verás a las personas mucho más interesadas en esta idea.


1
1) Bien, admito que he estado posponiendo el tema de la sincronización por un tiempo. Cuando digo que 'dos ​​tareas competirán', ese no es mi diseño, es principalmente una observación: dl.dropboxusercontent.com/u/27714141/… - No estoy seguro de qué tipo de brujería JavaScriptCore está realizando aquí, pero no debería ' ¿Esta cadena se corrompe si no fuera inherentemente atómica? 2) Estoy totalmente en desacuerdo. Esta es la razón por la que JS es considerado como un lenguaje de juguete. 3) Las promesas de ES6 serían mucho más eficaces si se implementan utilizando un programador de agrupación de subprocesos.
voodooattack

13
@voodooattack "Esta es la razón por la que JS es considerado como un lenguaje de juguete". No, esta es la razón por la que lo consideras un lenguaje de juguete. Millones de personas usan JS todos los días y están perfectamente felices con él, con sus defectos y todo. Asegúrese de resolver un problema que otras personas realmente tienen, que no se resuelve mejor simplemente cambiando los idiomas.
Chris Hayes

@ChrisHayes La pregunta es, ¿por qué tolerar sus deficiencias cuando puedo solucionarlas? ¿La concurrencia como característica mejoraría JavaScript?
voodooattack

1
@voodooattack Esa es la pregunta. ¿La concurrencia como característica mejoraría Javascript? Si puede obtener una respuesta de la comunidad a eso que no es "no", entonces tal vez esté en algo. Sin embargo, parece que el bucle de eventos y la delegación nativa de Node en subprocesos de trabajo para bloquear eventos son suficientes para la mayoría de lo que la gente necesita hacer. Creo que si las personas realmente necesitan Javascript enhebrado, usarán trabajadores de Javascript. Sin embargo, si puede encontrar una manera de hacer que los trabajadores trabajen con funciones de primera clase en lugar de archivos JS ... entonces podría estar realmente en algo.
lunchmeat317

77
@voodooattack Las personas que llaman a JavaScript un lenguaje de juguete no saben de qué están hablando. ¿Es el idioma de referencia para todo? Por supuesto que no, pero descartarlo así es ciertamente un error. Si desea disipar la noción de que JS es un lenguaje de juguete, haga una aplicación de producción no trivial o apunte a uno existente. Solo agregar concurrencia no cambiará las mentes de esas personas, de todos modos.
jpmc26

16

Solo adivinando aquí para demostrar un problema en su enfoque. No puedo probarlo con la implementación real ya que no hay ningún enlace en ninguna parte ...

Yo diría que es porque los invariantes no siempre se expresan por el valor de una variable, y 'una variable' no es suficiente para ser el alcance de un bloqueo en el caso general. Por ejemplo, imagine que tenemos un invariante que a+b = 0(el saldo de un banco con dos cuentas). Las dos funciones siguientes aseguran que el invariante se mantenga siempre al final de cada función (la unidad de ejecución en JS de un solo subproceso).

function withdraw(v) {
  a -= v;
  b += v;
}
function deposit(v) {
  b -= v;
  a += v;
}

Ahora, en su mundo multiproceso, ¿qué sucede cuando dos hilos se ejecutan withdrawy deposital mismo tiempo? Gracias Murphy ...

(Es posible que tenga un código que trate a + = y - = especialmente, pero eso no es de ayuda. En algún momento, tendrá un estado local en una función, y sin forma de 'bloquear' dos variables al mismo tiempo, su invariante ser violado)


EDITAR: si su código es semánticamente equivalente al código Go en https://gist.github.com/thriqon/f94c10a45b7e0bf656781b0f4a07292a , mi comentario es preciso ;-)


Este comentario es irrelevante, pero los filósofos son capaces de bloquear múltiples objetos, pero mueren de hambre porque los bloquean en un orden inconsistente.
Dietrich Epp

3
@voodooattack "Acabo de probar ..." ¿Nunca ha encontrado un orden de ejecución inconsistente con la programación de subprocesos? Varía de una carrera a otra, de una máquina a otra. Es inútil sentarse y ejecutar una sola prueba (¡o incluso cien pruebas!) Sin identificar el mecanismo de cómo se programan las operaciones.
jpmc26

44
@voodooattack El problema es que simplemente probar la concurrencia no es útil. Debe poder demostrar que el invariante se mantendrá, o el mecanismo nunca se puede confiar en la producción.
sapi

1
Pero no le ha dado al usuario ninguna herramienta para "usar el sistema de manera responsable" porque no hay absolutamente ningún mecanismo de bloqueo. Hacer que todo sea atómico da una ilusión de seguridad de subprocesos (y tomas el golpe de rendimiento de todo lo que es atómico, incluso cuando no necesitas acceso atómico), pero en realidad no resuelve la mayoría de los problemas de concurrencia como el que thriqon da aquí. Para otro ejemplo, intente iterar sobre una matriz en un hilo mientras otro hilo agrega o elimina elementos de la matriz. Para el caso, ¿qué te hace pensar que la implementación del motor de Array es incluso segura para subprocesos?
Zach Lipton

2
@voodooattack Entonces, si los usuarios solo pueden usar sus hilos para funciones sin datos compartidos o efectos secundarios (y será mejor que no los usen para otra cosa, porque como hemos visto aquí, no hay forma de garantizar la seguridad de los hilos), entonces, ¿qué valor está brindando a través de Web Workers (o una de las muchas bibliotecas que proporcionan una API más utilizable para Web Workers)? Y los Web Workers son mucho más seguros, porque hacen que sea imposible para el usuario usar el sistema "de manera irresponsable".
Zach Lipton el

15

Hace aproximadamente una década, Brendan Eich (el inventor de JavaScript) escribió un ensayo llamado Threads Suck , que es definitivamente uno de los pocos documentos canónicos de la mitología del diseño de JavaScript.

Si es correcto es otra pregunta, pero creo que tuvo una gran influencia en cómo piensa la comunidad JavaScript sobre la concurrencia.


31
La última persona de la que tomaría consejo es Brendan Eich. Toda su carrera se basa en la creación de JavaScript, que es tan malo que tenemos un montón de herramientas creadas para tratar de sortear su basura innata. Piensa en cuántas horas de desarrollador se han desperdiciado en el mundo gracias a él.
Phil Wright

12
Si bien entiendo por qué descartaría su opinión en general, e incluso su desdén por JavaScript, no entiendo cómo su perspectiva permitiría ignorar su experiencia en el dominio.
Billy Cravens

44
@BillyCravens jaja, incluso el libro canónico sobre Javascript: "Javascript las partes buenas " de Crockford regala el juego en el título. Trate las actitudes de Eich de la misma manera, solo quédese con lo que dice que es bueno :-)
gbjbaanb

13
@PhilWright: Su primera implementación de Javascript fue una variante de Lisp. Lo que para mí le gana un gran respeto. La decisión de su jefe de obligarlo a reemplazar la sintaxis de Lisp con una sintaxis tipo C no es su culpa. Javascript en su núcleo sigue siendo un tiempo de ejecución Lisp.
slebetman

77
@PhilWright: No deberías culpar a Eich por la porquería del lenguaje. Son errores en las primeras implementaciones y la necesidad de compatibilidad con versiones anteriores de un lenguaje web que impidió que JS madurara. Los conceptos centrales todavía forman un lenguaje maravilloso.
Bergi

8

El acceso atómico no se traduce en un comportamiento seguro para subprocesos.

Un ejemplo es cuando una estructura de datos global debe ser inválida durante una actualización, como volver a mostrar un hashmap (al agregar una propiedad a un objeto, por ejemplo) u ordenar una matriz global. Durante ese tiempo no puede permitir que ningún otro hilo acceda a la variable. Esto básicamente significa que necesita detectar los ciclos completos de lectura-actualización-escritura y bloquearlo. Si la actualización no es trivial, terminará en un territorio problemático detenido.

Javascript ha sido de un solo subproceso y sandboxed desde el principio y todo el código está escrito teniendo en cuenta estos supuestos.

Esto tiene una gran ventaja con respecto a los contextos aislados y permite que 2 contextos separados se ejecuten en hilos diferentes. También quiero decir que las personas que escriben JavaScript no necesitan saber cómo lidiar con las condiciones de carrera y otras fallas de varios hilos.


Es por eso que creo que la API del planificador debería ser más avanzada y utilizada internamente para promesas y funciones que no tienen efectos secundarios.
voodooattack

6

¿Su enfoque va a mejorar significativamente el rendimiento?

Dudoso. Realmente necesitas probar esto.

¿Su enfoque hará que sea más fácil / rápido escribir código?

Definitivamente no, el código multiproceso es muchas veces más difícil de corregir que el código de subproceso único.

¿Su enfoque será más robusto?

Los puntos muertos, las condiciones de carrera, etc. son una pesadilla para solucionar.


2
Intente construir un raytracer usando node.js, ahora intente nuevamente con hilos. El multiproceso no lo es todo.
voodooattack

8
@voodooattack, nadie en su sano juicio escribiría un rastreador de rayos usando javascript, con o sin subprocesos, porque es un algoritmo relativamente simple pero intensivo en computación que está mejor escrito en un lenguaje completamente compilado, preferiblemente uno con soporte SIMD . Para el tipo de problemas para los que se usa JavaScript, el multiproceso es más que suficiente.
Jan Hudec

@ JanHudec: Je, JS también recibirá soporte SIMD :-) hacks.mozilla.org/2014/10/introducing-simd-js
Bergi

2
@voodooattack Si no los conoce, eche un vistazo a SharedArrayBuffers . JavaScript está obteniendo más construcciones de concurrencia, pero se están agregando con extrema precaución, abordando puntos de dolor específicos, tratando de minimizar la toma de malas decisiones de diseño con las que tendremos que vivir durante años.
REINSTATE MONICA -Jeremy Banks

2

Su implementación no se trata solo de introducir concurrencia, sino de introducir una forma específica de implementar concurrencia, es decir, concurrencia por estado mutable compartido. En el transcurso de la historia, las personas han usado este tipo de concurrencia y esto ha llevado a muchos tipos de problemas. Por supuesto, puede crear programas simples que funcionen perfectamente con el uso de concurrencia de estado mutable compartida, pero la prueba real de cualquier mecanismo no es lo que puede hacer, sino que puede escalar a medida que el programa se vuelve complejo y se agregan más y más funciones al programa. Recuerde que el software no es algo estático que construye una vez y termina con él, sino que sigue evolucionando con el tiempo y si algún mecanismo o concepto puede '

Puede ver otros modelos de concurrencia (por ejemplo, pasar mensajes) para ayudarlo a descubrir qué tipo de beneficios proporcionan esos modelos.


1
Creo que las promesas de ES6 se beneficiarían del modelo de mi implementación, ya que no compiten por el acceso.
voodooattack

Eche un vistazo a las especificaciones de WebWorkers. Hay paquetes npm que proporcionan su implementación, pero puede implementarlos como el núcleo de su motor en lugar de un paquete
Ankur

JavaScriptCore (la implementación del kit web de JS que estoy usando) ya los implementa, es solo un indicador de compilación.
voodooattack

Okay. Los WebWorkers son concurrencia con el paso de mensajes. Puede probar su ejemplo de raytracer con ellos y compararlo con el enfoque de estado mutable.
Ankur

44
El paso de mensajes siempre se implementa sobre el estado mutable, lo que significa que sería más lento. No veo tu punto aquí. : - /
voodooattack

1

Esto es necesario La falta de un mecanismo de concurrencia de bajo nivel en el nodo js limita sus aplicaciones en campos como matemática y bioinformática, etc. Además, la concurrencia con hilos no necesariamente entra en conflicto con el modelo de concordancia predeterminado utilizado en el nodo. Existen semánticas bien conocidas para el subprocesamiento en un entorno con un bucle de eventos principal, como los marcos ui (y nodejs), y aunque definitivamente son demasiado complejos para la mayoría de las situaciones, todavía tienen usos válidos.

Claro, su aplicación web promedio no requerirá hilos, pero intente hacer algo un poco menos convencional, y la falta de una primitiva de concurrencia de bajo nivel de sonido lo alejará rápidamente de algo que sí lo ofrece.


44
Pero "matemáticas y bioinformática" estaría mejor escrito en C #, JAVA o C ++
Ian

En realidad, Python y Perl son los idiomas dominantes en esas áreas.
dryajov

1
En realidad, la razón principal por la que estoy implementando esto es por las aplicaciones de aprendizaje automático. Entonces tienes un punto.
voodooattack

@dryajov Alégrate de no saber qué tan grande es FORTRAN en algunas de las áreas de ciencias computacionales ...
cmaster

@dryajov Debido a que son más accesibles para los humanos que tal vez no son programadores a tiempo completo, no porque sean inherentemente mejores en la secuenciación del genoma: tenemos lenguajes especialmente diseñados como R y recopilamos langs científicos como Fortran para eso.
gato

0

Realmente creo que es porque es una idea diferente y poderosa. Estás yendo en contra de los sistemas de creencias. Las cosas se vuelven aceptadas o populares a través de la red y no afectan por méritos. Además, nadie quiere adaptarse a una nueva pila. Las personas rechazan automáticamente cosas que son demasiado diferentes.

Si puede encontrar una manera de convertirlo en un módulo npm normal que parezca poco probable, entonces puede hacer que algunas personas lo usen.


¿Quiere decir que los programadores de JS están bloqueados por el proveedor a npm?
voodooattack

3
La pregunta es sobre un nuevo sistema de tiempo de ejecución y no un módulo npm y también la concurrencia de estado compartido mutable es realmente una vieja idea.
Ankur

1
@voodooattack Quiero decir que están bloqueados por el enfoque que ya es popular y que será casi imposible superar el sesgo del statu quo.
Jason Livesay
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.