Saltar al contenido

¿Ruby tiene subprocesos múltiples reales?

Te doy la bienvenida a nuestra comunidad, ahora vas a encontrar la solucíon a lo que estás buscando.

Solución:

Actualizado con el comentario de Jörg de septiembre de 2011

Pareces estar confundiendo dos muy cosas diferentes aquí: el lenguaje de programación Ruby y el modelo de subprocesamiento específico de una implementación específica del lenguaje de programación Ruby. Actualmente hay alrededor de 11 implementaciones diferentes del lenguaje de programación Ruby, con muy modelos de roscado diferentes y únicos.

(Desafortunadamente, solo dos de esas 11 implementaciones están realmente listas para su uso en producción, pero para fin de año ese número probablemente aumentará a cuatro o cinco). (Actualizar: ahora son las 5: MRI, JRuby, YARV (el intérprete de Ruby 1.9), Rubinius y IronRuby).

  1. La primera implementación en realidad no tiene un nombre, lo que hace que sea bastante incómodo referirse a ella y es realmente molesto y confuso. A menudo se le conoce como “Ruby”, lo cual es aún más molesto y confuso que no tener nombre, porque conduce a una confusión infinita entre las características del lenguaje de programación Ruby y una implementación de Ruby en particular.

    A veces también se le llama “MRI” (para “Implementación Ruby de Matz”), CRuby o MatzRuby.

    MRI implementa Ruby Threads como Green Threads dentro de su intérprete. Desafortunadamente, no permite que esos subprocesos se programen en paralelo, solo pueden ejecutar un subproceso a la vez.

    Sin embargo, cualquier número de subprocesos C (subprocesos POSIX, etc.) puede ejecutarse en paralelo con Ruby Thread, por lo que las bibliotecas C externas o las extensiones MRI C que crean subprocesos propios aún pueden ejecutarse en paralelo.

  2. La segunda implementación es YARV (abreviatura de “Yet Another Ruby VM”). YARV implementa Ruby Threads como POSIX o Windows NT Threads, sin embargo, utiliza un bloqueo de intérprete global (GIL) para garantizar que solo se pueda programar realmente un Ruby Thread a la vez.

    Como MRI, hilos C pueden en realidad se ejecutan en paralelo a Ruby Threads.

    En el futuro, es posible que el GIL podría se dividen en bloqueos más detallados, lo que permite que más y más código se ejecute en paralelo, pero eso está tan lejos que ni siquiera planificado todavía.

  3. JRuby implementa Ruby Threads como Native Threads, donde “Native Threads” en el caso de JVM significa obviamente “JVM Threads”. JRuby no les impone ningún bloqueo adicional. Entonces, si esos subprocesos realmente pueden ejecutarse en paralelo depende de la JVM: algunas JVM implementan subprocesos JVM como subprocesos del sistema operativo y algunos como subprocesos verdes. (Las JVM convencionales de Sun / Oracle utilizan exclusivamente subprocesos del sistema operativo desde JDK 1.3)

  4. XRuby también implementa Ruby Threads como JVM Threads. Actualizar: XRuby está muerto.

  5. IronRuby implementa Ruby Threads como Native Threads, donde “Native Threads” en el caso de CLR significa obviamente “CLR Threads”. IronRuby no les impone ningún bloqueo adicional, por lo que deben ejecutarse en paralelo, siempre que su CLR lo admita.

  6. Ruby.NET también implementa Ruby Threads como CLR Threads. Actualizar: Ruby.NET está muerto.

  7. Rubinius implementa Ruby Threads como Green Threads dentro de su máquina virtual. Más precisamente: Rubinius VM exporta una construcción de flujo de control de concurrencia / paralelismo / no local muy liviana y muy flexible, llamada “Tarea”, y todas las demás construcciones de concurrencia (subprocesos en esta discusión, pero también continuaciones, actores y otras cosas ) se implementan en Ruby puro, usando Tasks.

    Rubinius no puede (actualmente) programar subprocesos en paralelo, sin embargo, agregar que no es un gran problema: Rubinius ya puede ejecutar varias instancias de VM en varios subprocesos POSIX en paralelo, dentro de un proceso de Rubinius. Dado que los subprocesos se implementan en Ruby, pueden, como cualquier otro objeto de Ruby, ser serializados y enviados a una máquina virtual diferente en un subproceso POSIX diferente. (Ese es el mismo modelo que utiliza BEAM Erlang VM para la concurrencia SMP. Ya está implementado para Rubinius Actors).

    Actualizar: La información sobre Rubinius en esta respuesta es sobre Shotgun VM, que ya no existe. La “nueva” máquina virtual C ++ no utiliza subprocesos verdes programados en varias máquinas virtuales (es decir, estilo Erlang / BEAM), utiliza una máquina virtual única más tradicional con varios modelos de subprocesos del sistema operativo nativo, como el empleado por, digamos, CLR, Mono y prácticamente todas las JVM.

  8. MacRuby comenzó como un puerto de YARV sobre Objective-C Runtime y CoreFoundation y Cocoa Frameworks. Ahora se ha diferenciado significativamente de YARV, pero AFAIK, actualmente todavía comparte el mismo modelo de subprocesamiento con YARV.
    Actualizar: MacRuby depende del recolector de basura de las manzanas, que se declara obsoleto y se eliminará en versiones posteriores de MacOSX, MacRuby no está muerto.

  9. Cardinal es una implementación de Ruby para Parrot Virtual Machine. Aún no implementa subprocesos, sin embargo, cuando lo haga, probablemente los implemente como Parrot Threads. Actualizar: Cardinal parece muy inactivo / muerto.

  10. MagLev es una implementación de Ruby para GemStone / S Smalltalk VM. No tengo información sobre qué modelo de subprocesamiento usa GemStone / S, qué modelo de subprocesamiento usa MagLev o incluso si los subprocesos están implementados todavía (probablemente no).

  11. HotRuby es no una implementación completa de Ruby propia. Es una implementación de una máquina virtual de código de bytes YARV en JavaScript. HotRuby no admite subprocesos (¿todavía?) Y cuando lo haga, no podrán ejecutarse en paralelo, porque JavaScript no tiene soporte para true paralelismo. Sin embargo, existe una versión ActionScript de HotRuby, y ActionScript podría admitir el paralelismo. Actualizar: HotRuby está muerto.

Desafortunadamente, solo dos de estas 11 implementaciones de Ruby están realmente listas para producción: MRI y JRuby.

Entonces, si quieres true subprocesos paralelos, JRuby es actualmente su única opción, no es que sea mala: JRuby es en realidad más rápido que la resonancia magnética y posiblemente más estable.

De lo contrario, la solución “clásica” de Ruby es utilizar procesos en lugar de subprocesos para el paralelismo. La biblioteca de Ruby Core contiene el Process módulo con el Process.fork
método que hace que sea muy fácil bifurcar otro proceso de Ruby. Además, la biblioteca estándar de Ruby contiene la biblioteca distribuida de Ruby (dRuby / dRb), que permite que el código de Ruby se distribuya trivialmente en múltiples procesos, no solo en la misma máquina sino también en la red.

Ruby 1.8 solo tiene subprocesos verdes, no hay forma de crear un subproceso real “a nivel del sistema operativo”. Pero, ruby ​​1.9 tendrá una nueva característica llamada fibras, que le permitirá crear subprocesos reales a nivel de sistema operativo. Desafortunadamente, Ruby 1.9 todavía está en beta, está programado que sea estable en un par de meses.

Otra alternativa es utilizar JRuby. JRuby implementa subprocesos como theads a nivel de sistema operativo, no hay “subprocesos verdes” en él. La última versión de JRuby es 1.1.4 y es equivalente a Ruby 1.8

Depende de la implementación:

  • La resonancia magnética no tiene, YARV está más cerca.
  • JRuby y MacRuby lo han hecho.

Ruby tiene cierres como Blocks, lambdas y Procs. Para aprovechar al máximo los cierres y múltiples núcleos en JRuby, los ejecutores de Java son útiles; para MacRuby me gustan las colas de GCD.

Tenga en cuenta que, al poder crear subprocesos reales de “nivel de SO” no implica que pueda utilizar varios núcleos de CPU para el procesamiento en paralelo. Mire los ejemplos a continuación.

Esta es la salida de un programa Ruby simple que usa 3 hilos usando Ruby 2.1.0:

([email protected] ~)$ ps -M 69877
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 69877 s002    0.0 S    31T   0:00.01   0:00.04 /Users/jalcazar/.rvm/rubies/ruby-2.1.0/bin/ruby threads.rb
   69877         0.0 S    31T   0:00.01   0:00.00 
   69877        33.4 S    31T   0:00.01   0:08.73 
   69877        43.1 S    31T   0:00.01   0:08.73 
   69877        22.8 R    31T   0:00.01   0:08.65 

Como puede ver aquí, hay cuatro subprocesos del sistema operativo, sin embargo, solo el que tiene el estado R Esta corriendo. Esto se debe a una limitación en cómo se implementan los hilos de Ruby.


Mismo programa, ahora con JRuby. Puedes ver tres hilos con estado R, lo que significa que se ejecutan en paralelo.

([email protected] ~)$ ps -M 72286
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 72286 s002    0.0 S    31T   0:00.01   0:00.01 /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/bin/java -Djdk.home= -Djruby.home=/Users/jalcazar/.rvm/rubies/jruby-1.7.10 -Djruby.script=jruby -Djruby.shell=/bin/sh -Djffi.boot.library.path=/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni/Darwin -Xss2048k -Dsun.java.command=org.jruby.Main -cp  -Xbootclasspath/a:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jruby.jar -Xmx1924M -XX:PermSize=992m -Dfile.encoding=UTF-8 org/jruby/Main threads.rb
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    33T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.09   0:02.34 
   72286         7.9 S    31T   0:00.15   0:04.63 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.04   0:01.68 
   72286         0.0 S    31T   0:00.03   0:01.54 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.01   0:00.01 
   72286         0.0 S    31T   0:00.00   0:00.01 
   72286         0.0 S    31T   0:00.00   0:00.03 
   72286        74.2 R    31T   0:09.21   0:37.73 
   72286        72.4 R    31T   0:09.24   0:37.71 
   72286        74.7 R    31T   0:09.24   0:37.80 

El mismo programa, ahora con MacRuby. También hay tres subprocesos que se ejecutan en paralelo. Esto se debe a que los subprocesos de MacRuby son subprocesos POSIX (subprocesos reales de “nivel de SO”) y no hay GVL

([email protected] ~)$ ps -M 38293
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 38293 s002    0.0 R     0T   0:00.02   0:00.10 /Users/jalcazar/.rvm/rubies/macruby-0.12/usr/bin/macruby threads.rb
   38293         0.0 S    33T   0:00.00   0:00.00 
   38293       100.0 R    31T   0:00.04   0:21.92 
   38293       100.0 R    31T   0:00.04   0:21.95 
   38293       100.0 R    31T   0:00.04   0:21.99 

Una vez más, el mismo programa pero ahora con la vieja resonancia magnética. Debido al hecho de que esta implementación usa hilos verdes, solo aparece un hilo

([email protected] ~)$ ps -M 70032
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 70032 s002  100.0 R    31T   0:00.08   0:26.62 /Users/jalcazar/.rvm/rubies/ruby-1.8.7-p374/bin/ruby threads.rb

Si está interesado en Ruby multi-threading, puede encontrar mi informe Depurar programas paralelos usando controladores de bifurcación interesante.
Para obtener una descripción general más general de los componentes internos de Ruby Ruby bajo un microscopio es una buena lectura.
Además, Ruby Threads y Global Interpreter Lock en C en Omniref explica en el código fuente por qué los hilos de Ruby no se ejecutan en paralelo.

Reseñas y puntuaciones del tutorial

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *