2010-10-25 14 views
6

Estaba investigando las capacidades de procesamiento paralelo/asíncrono de Ruby y leí muchos artículos y publicaciones de blog. Miré a través de EventMachine, Fibras, Revactor, Reia, etc, etc. Por desgracia, yo no era capaz de encontrar una solución sencilla, eficaz (y no-IO-bloqueo) para este caso de uso muy sencillo:Simultaneidad de Ruby/procesamiento asíncrono (con caso de uso simple)

File.open('somelogfile.txt') do |file| 
    while line = file.gets  # (R) Read from IO 
    line = process_line(line) # (P) Process the line 
    write_to_db(line)   # (W) Write the output to some IO (DB or file) 
    end 
end 

es que se puede ver, mi pequeño script está llevando a cabo tres operaciones leen (R ), proceso (P ) & de escritura (W ). Vamos a suponer que - por simplicidad - que cada operación tiene exactamente 1 unidad de tiempo (por ejemplo, 10 ms), por lo tanto, el código actual podría hacer algo como esto (5 líneas):

Time:  123456789(15 units in total) 
Operations: RPWRPWRPWRPWRPW 

Pero, me gustaría que haga algo como esta:

Time:  1234567 (7 units in total) 
Operations: RRRRR 
      PPPPP 
       WWWWW 

Obviamente, podría correr tres procesos (lector, procesador & escritor) y pasar leer líneas de lector en la cola del procesador y luego pasar líneas procesadas en la cola de escritor (todos coordinados a través de por ejemplo RabbitMQ) . Pero, el caso de uso es tan simple, simplemente no se siente bien.

¿Alguna pista sobre cómo esto podría hacerse (sin cambiar de Ruby a Erlang, Closure o Scala)?

+1

¿Se deben llamar las escrituras en el mismo orden en que se leyeron? –

+0

No, ese es el punto en el que pueden ser completamente asincrónicos. – Dim

Respuesta

1

Echa un vistazo a melocotón (http://peach.rubyforge.org/). Hacer un "cada" paralelo no podría ser más simple. Sin embargo, como dice la documentación, deberá ejecutar bajo JRuby para usar el enrutamiento nativo de la JVM.

Consulte la respuesta de Jorg Mittag al this SO question para obtener muchos detalles sobre las capacidades de subprocesamiento múltiple de varios intérpretes de Ruby.

+0

Hmm, el melocotón no es realmente lo que estoy buscando. No quiero ejecutar el RPW en paralelo, quiero separar las 3 tareas entre sí y ejecutarlas de forma asíncrona. La respuesta de Jorg Mittag es una gran introducción. Soy muy consciente de las opciones ofrecidas, pero ninguna de ellas parece tener una respuesta para mi problema. – Dim

3

Si necesita que sea verdaderamente paralelo (a partir de un único proceso) creo que tendrá que usar JRuby para obtener hilos nativos verdaderos y no GIL.

Puede usar algo como DRb para distribuir el procesamiento a través de múltiples procesos/núcleos, pero para su caso de uso esto es un poco demasiado. En su lugar, podría intentar tener múltiples procesos se comunican mediante tuberías:

$ cat somelogfile.txt | ruby ./proc-process | ruby ./proc-store 

En este escenario cada pieza es su propio proceso que se pueden ejecutar en paralelo, pero se comunican usando STDIN/STDOUT. Este es probablemente el enfoque más fácil (y más rápido) para su problema.

# proc-process 
while line = $stdin.gets do 
    # do cpu intensive stuff here 
    $stdout.puts "data to be stored in DB" 
    $stdout.flush # this is important 
end 

# proc-store 
while line = $stdin.gets do 
    write_to_db(line) 
end 
+1

Pensé que el GIL de Ruby 1.9 le permite hacer cosas de CPU en un subproceso mientras que otro subproceso hace E/S, es decir, solo prohíbe dos subprocesos haciendo cosas de CPU. –

+0

¿Estás hablando de fibras?Mi comprensión limitada de Fibras es que en lugar de subprocesos que tienen una cantidad compartida de tiempo de CPU, el código expulsa explícitamente el procesamiento a Fiber, que puede manejar la operación de bloqueo IO e inmediatamente regresar al código de llamada. Esto reduce la cantidad de tiempo que pasa esperando, pero no creo que le permita abarcar más de una CPU por proceso. Creo que GIL significa que solo se puede ejecutar un hilo de ejecución en cualquier punto del tiempo. http://www.igvita.com/2009/05/13/fibers-cooperative-scheduling-in-ruby/ – JEH

+2

Utilizar pipes es una buena solución para dividir el problema en 3 procesos separados, pero no es asincrónico. De hecho, es una "solución de Ruby", por lo tanto, es bastante difícil de implementar dentro del alcance de una aplicación más grande. El "problema" que he descrito anteriormente es un ejemplo simple de procesamiento impulsado por IO. Estoy tratando de entender de qué es capaz Ruby en esta área y de lo que podría faltar. – Dim