2008-09-11 8 views

Respuesta

586

Actualizado con Sept 2011 Comentario de Jörg

Usted parece ser confuso dos cosas muy diferentes aquí: la Rubí Lenguaje de programación y el modelo de hilos específico de una implementación específica del lenguaje de programación Ruby. Hay actualmente en torno a 11 implementaciones diferentes del lenguaje de programación Ruby , con muy modelos diferentes y únicos de roscado .

(Por desgracia, sólo dos de esos 11 implementaciones son en realidad listo para su uso en producción, pero para el final del año ese número probablemente se irá hasta 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 para referirse a la misma y es muy molesto y confuso . A menudo se lo conoce como "Ruby", que es incluso más molesto y confuso que no tener nombre, porque conduce a una confusión interminable entre las características del lenguaje de programación Ruby y una implementación particular de Ruby.

    También se lo denomina a veces "MRI" (para "Implementación de Matz's Ruby "), CRuby o MatzRuby.

    MRI implements Ruby Threads as Green Threads within its interpreter. Desafortunadamente, no permite que esos hilos se programen en paralelo, solo pueden ejecutar un hilo en un tiempo .

    Sin embargo, cualquier número de C hilos (hilos POSIX etc.) puede funcionar en paralelo al hilo de Ruby, por lo bibliotecas de C externos, o MRI Extensiones C que crear hilos de su propia siguen funcionando en paralelo .

  2. La segunda implementación es YARV (abreviatura de "Yet Another Ruby VM"). YARV implements Ruby Threads as POSIX or Windows NT Threads, sin embargo, utiliza un Intérprete global Bloqueo (GIL) para garantizar que solo un subproceso de Ruby realmente puede ser programado en cualquier momento.

    Al igual que MRI, C Temas puede ejecutar en realidad paralelo a Ruby Threads.

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

  3. JRubyimplements Ruby Threads as Native Threads, donde los "hilos" nativos en caso de que la JVM, obviamente, significa "JVM hilos". JRuby no les impone ningún bloqueo adicional. Por lo tanto, si esos subprocesos realmente pueden ejecutarse en paralelo depende de la JVM: algunas JVM implementan subprocesos de JVM como subprocesos de sistema operativo y algunos como subprocesos verdes. (Las JVM principales de Sun/Oracle utilizan exclusivamente subprocesos del sistema operativo desde JDK 1.3)

  4. XRuby también implements Ruby Threads as JVM Threads. Actualización: XRuby está muerto.

  5. IronRubyimplements Ruby Threads as Native Threads, donde los "hilos" nativos en caso de que el CLR, obviamente, significa "CLR hilos". IronRuby no impone ningún bloqueo adicional en ellos, , por lo tanto, deben ejecutarse en paralelo, siempre que su CLR sea compatible con .

  6. Ruby.NET también implements Ruby Threads as CLR Threads. Actualización: Ruby.NET está muerto.

  7. Rubiniusimplements Ruby Threads as Green Threads within its Virtual Machine. Más precisamente: la Rubinius VM exporta una, muy flexible concurrencia/paralelismo/constructo de flujo de control no local muy ligero, llamado un "Task", y todas las demás construcciones de concurrencia (Temas en esta discusión, pero también Continuations, Actors y otras cosas) se implementan en Ruby puro, usando Tareas.

    Rubinius no puede (actualmente) horario Temas en paralelo, sin embargo, la adición de que no es demasiado de un problema: Rubinius puede ya run several VM instances in several POSIX Threads in parallel, dentro de un proceso de Rubinius. Dado que los hilos son realmente implementados en Ruby, pueden, como cualquier otro objeto Ruby , ser serializados y enviados a una máquina virtual diferente en un hilo POSIX diferente . (Es el mismo modelo que el BEAM Erlang VM utiliza para la concurrencia de SMP.Ya es implemented for Rubinius Actors.)

    Actualización: La información sobre Rubinius en esta respuesta es sobre la escopeta VM, que ya no existe. La "nueva" máquina virtual C++ no usa subprocesos verdes programados en varias máquinas virtuales (es decir, estilo Erlang/BEAM), utiliza una máquina virtual individual más tradicional con múltiples modelos de subprocesos nativos del sistema operativo, como el empleado por, por ejemplo, CLR, Mono y prácticamente todas las JVM.

  8. MacRuby comenzó como un puerto de YARV en la parte superior de Objective-C Runtime y CoreFoundation y Cocoa Frameworks. Es ahora ha divergido significativamente de YARV, pero AFAIK es actualmente todavía shares the same Threading Model with YARV. Actualización: MacRuby depende del recolector de basura de 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 el Parrot Virtual Machine. Sin embargo, aún no implementa subprocesos, cuando lo hace, probablemente los implementará como Parrot Threads. Actualización: Cardinal parece muy inactivo/muerto.

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

  11. HotRuby es no una implementación completa de Ruby de su propia . Es una implementación de una VM de bytecode de YARV en JavaScript. HotRuby no admite subprocesos (¿todavía?) Y cuando lo hace , no podrán ejecutarse en paralelo, porque JavaScript no admite el paralelismo verdadero. Sin embargo, existe una versión de HotRuby de ActionScript , y ActionScript en realidad podría ser compatible con el paralelismo. Actualización: HotRuby está muerto.

Por desgracia, sólo dos de estos 11 Implementaciones de Ruby son realmente listo para la producción: la resonancia magnética y JRuby.

Por lo tanto, si quieres verdaderos hilos paralelos, JRuby es actualmente su única opción - no es que eso es una mala: JRuby es realmente más rápido que la RM, y posiblemente más estable.

De lo contrario, la solución "clásica" de Ruby es usar los procesos en lugar de hilos para el paralelismo. La biblioteca Ruby Core contiene el Process module con el Process.fork method, lo que hace que sea más fácil bifurcar otro proceso Ruby . Además, la Biblioteca estándar de Ruby contiene la biblioteca Distributed Ruby (dRuby/dRb), que permite que el código Ruby se distribuya de manera trivial en múltiples procesos, no en solo en la misma máquina pero también a través de la red.

+1

pero El uso de horquilla romperá el uso en jruby ... simplemente diciendo – akostadinov

+0

Esta es una gran respuesta. Sin embargo, está sujeto a mucha podredumbre de enlace. Sin embargo, no sé dónde se han movido estos recursos. – BlackVegetable

25

Ruby 1.8 solo tiene subprocesos verdes, no hay forma de crear un subproceso real "nivel OS". Pero, ruby ​​1.9 tendrá una nueva característica llamada fibras, que le permitirá crear hilos de nivel de sistema operativo reales. Desafortunadamente, Ruby 1.9 aún está en beta, está programado para ser estable en un par de meses.

Otra alternativa es utilizar JRuby. JRuby implementa threads como theads de nivel de 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

+35

Es falso que Ruby 1.8 tiene sólo hilos verdes, varias implementaciones de Ruby 1.8 tienen subprocesos nativos: JRuby, XRuby, Ruby.NET y IronRuby. Las fibras no permiten la creación de subprocesos nativos, son * más ligeros * que los subprocesos. En realidad, son semi-corotines, es decir, son cooperativos. –

+18

Creo que es bastante obvio por la respuesta de Josh que quiere decir Ruby 1.8 the runtime, ak.a. MRI, y no Ruby 1.8 el lenguaje, cuando dice Ruby 1.8. – Theo

4

¿Qué le parece usar drb? No es un multi-threading real sino la comunicación entre varios procesos, pero ahora puedes usarlo en 1.8 y su fricción es bastante baja.

1

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

1

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

También, si está interesado en el futuro del roscado bajo Ruby, puede encontrar este article útil.

+0

JRuby es una buena opción. Para el procesamiento paralelo usando procesos me gusta https://github.com/grosser/parallel 'Parallel.map (['a', 'b', 'c'],: in_processes => 3) {...' – user454322

7

Depende de la aplicación:

  • RMI no tiene, es YARV cerca.
  • JRuby y MacRuby tienen.




Ruby tiene closures como Blocks, lambdas y Procs. Para aprovechar al máximo los cierres y núcleos múltiples en JRuby, Java's executors son útiles; para MacRuby Me gusta GCD's queues.

Tenga en cuenta que, al poder crear roscas reales de "nivel del sistema operativo", no implica que pueda utilizar múltiples núcleos de CPU para el procesamiento en paralelo. Mira los ejemplos a continuación.

Ésta es la salida de a simple Ruby program which uses 3 threads usando Rubí 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 se puede ver aquí, hay cuatro hilos del sistema operativo, sin embargo, sólo el uno con el estado R se está ejecutando. Esto se debe a una limitación en la forma en que se implementan los hilos de Ruby.



mismo programa, ahora con JRuby. Puede ver tres hilos con el estado R, lo que significa que se están ejecutando 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 hilos que se ejecutan en paralelo. Esto se debe a MacRuby threads are POSIX threads (reales hilos "a nivel de sistema operativo") y existe no 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 el buen viejo RMI. Debido al hecho de que esta aplicación utiliza los green-hilos, sólo un hilo aparece

([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 usted está interesado en Ruby multi-threading es posible encontrar mi informe Debugging parallel programs using fork handlers interesante.
Para una visión más general de Ruby interno Ruby Under a Microscope es una buena lectura.
Además, Ruby Threads and the Global Interpreter Lock in C en Omniref explica en el código fuente por qué los subprocesos de Ruby no se ejecutan en paralelo.

2

Voy a dejar que el "Monitor del sistema" responda esta pregunta. Estoy ejecutando el mismo código (a continuación, que calcula números primos) con 8 subprocesos Ruby ejecutándose en una máquina i7 (4 núcleos con hiperhilo) en ambos casos ... la primera ejecución es con:

jruby 1.5.6 (rubí 1.8.7 Patchlevel 249) (2014-02-03 6586) (OpenJDK 64-Bit servidor VM 1.7.0_75) [amd64-java]

El segundo es con:

rubí 2.1.2p95 (2014-05-08) [x86_64-linux-gnu]

Curiosamente, la CPU es más alta para los subprocesos JRuby, pero el tiempo de finalización es un poco más corto para el interpretado Ruby. Es un poco difícil saber a partir de la gráfica, pero el segundo (Rubí interpretada) de ejecución utiliza alrededor de 1/2 de la CPU (sin hyperthreading?)

enter image description here

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 
0

Debido a que no se pudo editar la respuesta, por lo agregue una nueva respuesta aquí.

actualización (2017-05-08)

Este artículo es muy antigua, y la información no se sigue (2017) la banda de rodamiento actual , A continuación se presenta algún suplemento:

  1. Opal es un compilador fuente a fuente de Ruby a JavaScript. También tiene una implementación de Ruby corelib, es un desarrollo actual muy activo, y existe una gran cantidad de framework frontend trabajando en él. y listo para producción. Como base en javascript, no admite subprocesos paralelos.

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