¿Ruby tiene multihilo real?


Conozco el subproceso "cooperativo" de ruby usando hilos verdes. ¿Cómo puedo crear subprocesos reales "a nivel de sistema operativo" en mi aplicación para hacer uso de múltiples núcleos de cpu para el procesamiento?

Author: user454322, 2008-09-11

9 answers

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

Usted parece estar confundiendo dos muy cosas diferentes aquí: el Lenguaje de programación Ruby y el modelo de threading específico de one implementación específica del Lenguaje de Programación Ruby. Alli actualmente hay alrededor de 11 implementaciones diferentes del Ruby Lenguaje de programación, con muy threading diferente y único modelo.

(Desafortunadamente, solo dos de esas 11 implementaciones son en realidad ¿Listos? para su uso en la producción, pero al final del año ese número probablemente subirá a cuatro o cinco.) (Actualización : ahora es 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, que hace que sea bastante incómodo referirse a él y es realmente molesto y confuso. Lo más a menudo se conoce como "Rubí", que es incluso más molesto y confuso que no tener nombre, porque conduce a un sinfín confusión entre las características del Ruby Lenguaje de Programación y una implementación particular de Ruby.

    A veces también se le llama " MRI "(por " Rubí de Matz Implementación"), CRuby o MatzRuby.

    MRI implementa Hilos de Rubí como Hilos Verdes dentro de su interpreter . Desafortunadamente, no permite esos hilos para ser programados en paralelo, solo pueden ejecutar un subproceso en un tiempo.

    Sin embargo, cualquier número de hilos de C (Hilos POSIX, etc.) puede funcionar en paralelo al hilo Ruby, por lo que las bibliotecas externas de C, o MRI Las extensiones 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 subprocesos Ruby como POSIX o Windows NT Threads , sin embargo, utiliza un Intérprete Global Bloquear (GIL) para asegurar que solo un hilo Ruby puede ser programado en cualquier momento.

    Como MRI, Hilos C puede en realidad se ejecutan en paralelo a los hilos de Ruby.

    En el futuro, es posible que el GIL podría romperse hacia abajo en cerraduras de grano más fino, lo que permite más y más código para ejecutar en paralelo, pero eso está tan lejos, es ni siquiera planeado todavía.

  3. JRuby implementa Subprocesos Ruby como Subprocesos Nativos , donde "Hilos nativos" en el caso de la JVM obviamente significa " JVM Hilo". JRuby no les impone ningún bloqueo adicional. Tan, si esos hilos se pueden ejecutar en paralelo depende de la JVM: algunas JVM implementan Subprocesos JVM como Subprocesos del sistema operativo y algunos como Hilos Verdes. (Los JVM principales de Sun / Oracle utilizan exclusivamente hilos de SO desde JDK 1.3)

  4. XRuby también implementa Subprocesos Ruby como Subprocesos JVM. Actualización : XRuby está muerto.

  5. IronRuby implementa Subprocesos Ruby como Subprocesos Nativos , donde "Hilos nativos" en caso de la CLR obviamente significa "CLR Hilos". IronRuby no impone ningún bloqueo adicional sobre ellos, por lo tanto, deben ejecutarse en paralelo, siempre y cuando su CLR soporte que.

  6. Ruby.NET también implementa Subprocesos Ruby como CLR Threads . Actualización: Ruby.NET está muerto.

  7. Rubinius implementa Hilos Ruby como Hilos Verdes dentro de su Máquina Virtual . Más precisamente: el Rubinius VM exporta un producto muy ligero, muy flexible concurrency / parallelism / non-local control-flow construct, called una " Task", y todas las demás construcciones de concurrencia (Subprocesos en esta discusión, pero también Continuaciones, Actores y otras cosas) se implementan en pure Ruby, usando Tareas.

    Rubinius no puede (actualmente) programar Subprocesos en paralelo, sin embargo, agregar eso no es demasiado problema: Rubinius puede ya ejecuta varias instancias de VM en varios subprocesos POSIX en paralelo , dentro un proceso Rubinius. Dado que los hilos son realmente implementado en Ruby, pueden, como cualquier otro Ruby objeto, serializado y enviado a una máquina virtual diferente en un Hilo POSIX. (Ese es el mismo modelo de la VIGA Erlang VM usos para la concurrencia SMP. It is already implemented for Rubinius Actors .)

    Update: La información sobre Rubinius en esta respuesta es sobre la máquina virtual Shotgun, que ya no existe. La" nueva " VM C++ no usa hilos verdes programado en varias máquinas virtuales (es decir, estilo Erlang/BEAM), utiliza una máquina virtual única más tradicional con múltiples modelos de subprocesos de sistema operativo nativo, al igual que el empleado por, digamos, el CLR, Mono y prácticamente todas las JVM.

  8. MacRuby comenzó como un puerto de YARV en la parte superior de la Objective - C Runtime and CoreFoundation and Cocoa Frameworks. Se ahora ha divergido significativamente de YARV, pero AFAIK que actualmente todavía comparte el mismo Modelo de roscado con YARV . Actualización: MacRuby depende del recolector de basura de apples que se declara obsoleto y se eliminará en versiones posteriores de MacOSX, MacRuby es no-muerto.

  9. Cardinal es una implementación de Ruby para el Loro Máquina virtual . Sin embargo, aún no implementa subprocesos, cuando lo haga, probablemente los implementará como Loro Threads . Actualización : Cardinal parece muy inactivo / muerto.

  10. MagLev es una implementación de Ruby para GemStone/S Smalltalk VM . No tengo información de qué modelo de roscado GemStone / S usos, qué enhebrado modelo MagLev utiliza o incluso si los hilos están implementados aún (probablemente no).

  11. HotRuby es no una implementación completa de Ruby de su propio. Es una implementación de una VM de bytecode YARV en JavaScript. HotRuby no admite hilos (¿todavía? y cuando lo hace, no serán capaces de ejecutar en paralelo, porque JavaScript no tiene apoyo para el verdadero paralelismo. Hay un ActionScript versión de HotRuby, sin embargo, y ActionScript en realidad podría apoya el paralelismo. Actualización: HotRuby está muerto.

Desafortunadamente, solo dos de estas 11 implementaciones de Ruby son actualmente lista para producción: MRI y JRuby.

Por lo tanto, si desea verdaderos hilos paralelos, JRuby es actualmente su la única opción-no es que sea mala: JRuby es realmente más rápido que la resonancia magnética, y podría decirse que más estable.

De lo contrario, la solución" clásica " de Ruby es usar procesos en lugar de hilos para paralelismo. La Biblioteca Ruby Core contiene la Process módulo con el Process.fork método que hace que sea muy fácil bifurcar otro Rubí proceso. Además, la Biblioteca Estándar de Ruby contiene Biblioteca distribuida de Ruby (dRuby / dRb), que permite Ruby código para ser distribuido trivialmente a través de múltiples procesos, no solo en la misma máquina, pero también a través de la red.

 593
Author: Jörg W Mittag,
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
2016-04-19 11:57:43

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 del sistema operativo. Desafortunadamente, Ruby 1.9 todavía está en beta, está programado para ser estable en un par de meses.

Otra alternativa es usar JRuby. JRuby implementa los hilos como teads a nivel del sistema operativo, no hay" hilos verdes " en él. La última versión de JRuby es 1.1.4 y es equivalente a Ruby 1.8

 27
Author: Josh Moore,
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
2008-09-11 09:05:37

Depende de la implementación:

  • RMI 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, ser capaz de crear hilos reales "a nivel del sistema operativo" no implica que puede utilizar varios núcleos de cpu para el procesamiento en paralelo. Mira los ejemplos a continuación.

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

(jalcazar@mac ~)$ 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 se está ejecutando el que tiene el estado R. Esto se debe a una limitación en cómo se implementan los subprocesos de Ruby.



El mismo programa, ahora con JRuby. Puedes ver tres hilos con state R, lo que significa que son corriendo en paralelo.

(jalcazar@mac ~)$ 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 hilos que se ejecutan en paralelo. Esto se debe a que los hilos MacRuby son hilos POSIX (subprocesos reales "a nivel del sistema operativo" ) y no hay GVL

(jalcazar@mac ~)$ 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 el buen viejo RMI. Debido al hecho de que esta implementación utiliza hilos verdes, solo aparece un hilo

(jalcazar@mac ~)$ 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 usted es interesado en Ruby multi-threading puede encontrar mi informe Depuración de programas paralelos utilizando controladores de horquilla interesante.
Para una visión más general de los internos de Ruby Rubí Bajo un Microscopio es una buena lectura.
Además, los subprocesos Ruby y el Bloqueo del Intérprete Global en C en Omniref explican en el código fuente por qué los subprocesos Ruby no se ejecutan en paralelo.

 8
Author: user454322,
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
2016-05-10 07:10:05

¿Qué tal usar drb? No es real multi-threading pero la comunicación entre varios procesos, pero se puede utilizar ahora en 1.8 y es bastante baja fricción.

 4
Author: ujh,
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
2008-09-11 11:57:27

Dejaré que el "Monitor del Sistema" responda esta pregunta. Estoy ejecutando el mismo código (abajo, que calcula números primos) con 8 subprocesos Ruby corriendo en una máquina i7 (4 hyperthreaded-core) en ambos casos... la primera ejecución es con:

Jruby 1.5.6 (ruby 1.8.7 patchlevel 249) (2014-02-03 6586) (OpenJDK 64-Bit Server VM 1.7.0_75) [amd64-java]

El segundo es con:

Ruby 2.1. 2p95 (2014-05-08) [x86_64-linux-gnu]

Curiosamente, la CPU es mayor para JRuby subprocesos, pero el tiempo de finalización es ligeramente más corto para el Ruby interpretado. Es un poco difícil de decir desde el gráfico, pero la segunda ejecución (interpretado Ruby) utiliza alrededor de 1/2 de las CPU (sin hyperthreading?)

introduzca la descripción de la imagen aquí

def eratosthenes(n)
  nums = [nil, nil, *2..n]
  (2..Math.sqrt(n)).each do |i|
    (i**2..n).step(i){|m| nums[m] = nil}  if nums[i]
  end
  nums.compact
end

MAX_PRIME=10000000
THREADS=8
threads = []

1.upto(THREADS) do |num|
  puts "Starting thread #{num}"
  threads[num]=Thread.new { eratosthenes MAX_PRIME }
end

1.upto(THREADS) do |num|
    threads[num].join
end
 3
Author: GroovyCakes,
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
2015-02-22 23:20:04

Si está utilizando MRI, entonces puede escribir el código enhebrado en C ya sea como una extensión o utilizando la gema ruby-inline.

 1
Author: ,
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
2008-09-11 18:50:51

Si realmente necesita paralelismo en Ruby para un sistema de nivel de Producción (donde no puede emplear una versión beta) los procesos son probablemente una mejor alternativa.
Pero, definitivamente vale la pena probar hilos bajo JRuby primero.

También si usted está interesado en el futuro de threading bajo Ruby, usted puede encontrar este artículo útil.

 1
Author: Pascal,
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
2008-09-11 19:10:36

Aquí hay algo de información sobre Rinda, que es la implementación de Ruby de Linda (parallel processing and distributed computing paradigm) http://charmalloc.blogspot.com/2009/12/linda-tuples-rinda-drb-parallel.html

 1
Author: Joe Stein,
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-01-01 00:24:29

Porque no se pudo editar esa respuesta, así que agrega una nueva respuesta aquí.

Actualizar(2017-05-08)

Este artículo es muy antiguo, y la información no es seguir actual (2017) tread, lo que sigue es algún suplemento:

  1. Opal es un compilador de Ruby a JavaScript de fuente a fuente. También tiene una implementación del Ruby corelib, actualmente es un desarrollador muy activo, y existe una gran cantidad de framework (frontend) trabajado en él. y producción lista. Porque basado en javascript, no soporta hilos paralelos.

  2. Truffleruby es una implementación de alto rendimiento del lenguaje de programación Ruby. Construido sobre el GraalVM por Oracle Labs, TruffleRuby es una bifurcación de JRuby, combinándolo con código del proyecto Rubinius, y también conteniendo código de la implementación estándar de Ruby, MRI, todavía en desarrollo vivo, no listo para producción. Esta versión ruby parece haber nacido para el rendimiento, no se si soporta hilos paralelos, pero creo que debería.

 0
Author: zw963,
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-08 14:26:53