Mínimo & # 8220; Cola de tareas & # 8221; con herramientas de Linux en stock para aprovechar la CPU multinúcleo

StackOverflow https://stackoverflow.com/questions/832253

  •  06-07-2019
  •  | 
  •  

Pregunta

¿Cuál es la forma mejor / más fácil de construir un sistema de cola de tareas mínimo para Linux usando bash y herramientas comunes?

Tengo un archivo con 9'000 líneas, cada línea tiene una línea de comando bash, los comandos son completamente independientes.

command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log
...

Mi caja tiene más de un núcleo y quiero ejecutar X tareas al mismo tiempo. Busqué en la web una buena manera de hacer esto. Aparentemente, muchas personas tienen este problema, pero hasta ahora nadie tiene una buena solución.

Sería bueno que la solución tuviera las siguientes características:

  • puede interpretar más de un comando (por ejemplo, command; command )
  • puede interpretar redirecciones de flujo en las líneas (por ejemplo, ls > /tmp/ls.txt )
  • solo usa herramientas comunes de Linux

Puntos de bonificación si funciona en otros clones Unix sin requisitos demasiado exóticos.

¿Fue útil?

Solución

¿Puedes convertir tu lista de comandos a un Makefile? Si es así, puede ejecutar " make -j X " ;.

Otros consejos

GNU Parallel http://www.gnu.org/software/parallel/ es una herramienta más general para paralelizar que PPSS.

Si el archivo de ejecución contiene:

command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log

puedes hacer:

cat runfile | parallel -j+0

que ejecutará un comando por núcleo de CPU.

Si sus comandos son tan simples como los anteriores, ni siquiera necesita runfile pero puede hacer:

seq 1 3 | parallel -j+0 'command {} > Logs/{}.log'

Si tiene más computadoras disponibles para hacer el procesamiento, puede consultar las opciones --sshlogin y --trc para GNU Parallel.

Bien, después de publicar la pregunta aquí, encontré el siguiente proyecto que parece prometedor: ppss .

Editar: no es exactamente lo que quiero, PPSS se centra en el procesamiento de "todos los archivos en el directorio A".

Bueno, esta es una pregunta divertida de todos modos.

Esto es lo que haría, suponiendo bash (1) , por supuesto.

  • calcula cuántos de estos comandos pueden ejecutarse de manera simultánea. No va a ser solo el número de núcleos; se suspenderán muchos comandos para E / S y ese tipo de cosas. Llame a ese número N. N = 15 por ejemplo.
  • configura un controlador de señal de captura para la señal SIGCHLD, que ocurre cuando finaliza un proceso secundario. trap signalHandler SIGCHLD
  • coloca tu lista de comandos en una tubería
  • escribe un bucle que lee stdin y ejecuta los comandos uno por uno, decrementando un contador. Cuando el contador es 0, espera s.
  • su controlador de señal, que se ejecuta en SIGCHLD, incrementa ese contador.

Entonces, ahora ejecuta los primeros comandos N , luego espera. Cuando el primer hijo termina, la espera regresa, lee otra línea, ejecuta un nuevo comando y espera nuevamente.

Ahora, este es un caso que se ocupa de muchos trabajos que terminan juntos. sospecho que puedes salirte con una versión más simple:

 N=15
 COUNT=N
 cat mycommands.sh | 
 while read cmd 
 do
   eval $cmd &
   if $((count-- == 0))
   then
       wait
   fi
 od

Ahora, este iniciará los primeros 15 comandos y luego ejecutará el resto de uno en uno a medida que finalice algún comando.

Una diversión similar de computación distribuida es la secuencia de comandos Mapreduce Bash:

http://blog.last.fm/2009/04 / 06 / mapreduce-bash-script

¡Y gracias por señalar ppss!

Puede usar el comando xargs , su --max-procs hace lo que quiere. Por ejemplo, la solución de Charlie Martin se convierte en xargs:

tr '\012' '\000' <mycommands.sh |xargs --null --max-procs=$X bash -c

detalles:

  • X es el número de procesos máx. Por ejemplo: X = 15. --max-procs está haciendo la magia
  • el primer tr está aquí para terminar líneas en bytes nulos para xargs - opción nula para que la redirección de comillas, etc. no se expanda incorrectamente
  • bash -c ejecuta el comando

Lo probé con este archivo mycommands.sh, por ejemplo:

date
date "+%Y-%m-%d" >"The Date".txt
wc -c <'The Date'.txt >'The Count'.txt

Este es un caso específico, pero si está tratando de procesar un conjunto de archivos y producir otro conjunto de archivos de salida, puede iniciar #cores número de procesos y verificar si existe un archivo de salida antes de procesarlo. El siguiente ejemplo convierte un directorio de archivos .m4b en archivos .mp3:

Simplemente ejecute este comando tantas veces como tenga núcleos:

ls * m4b | mientras se lee f; hacer prueba -f $ {f% m4b} mp3 || mencoder -of rawaudio " $ f " -oac mp3lame -ovc copy -o $ {f% m4b} mp3; hecho & amp;

Puede ver la cola de mis tareas escrita en bash: https://github.com/pavelpat/yastq

Cola de tareas + Paralelo + Adición dinámica

Usando un FIFO, este script se bifurca para procesar la cola. De esta manera, puede agregar comandos a la cola sobre la marcha (cuando la cola ya está iniciada).

Uso: ./queue Command [# of children] [Queue name]

Ejemplo, con 1 hilo:

./queue "sleep 5; echo ONE"
./queue "echo TWO"

Salida:

ONE
TWO

Ejemplo, con 2 hilos:

./queue "sleep 5; echo ONE" 2
./queue "echo TWO"

Salida:

TWO
ONE

Ejemplo, con 2 colas:

./queue "sleep 5; echo ONE queue1" 1 queue1
./queue "sleep 3; echo ONE queue2" 1 queue2

Salida:

ONE queue2
ONE queue1

El script (guárdelo como " cola " y chmod + x cola):

    #!/bin/bash

    #Print usage
    [[ $# -eq 0 ]] && echo Usage: <*> Command [# of children] [Queue name] && exit

    #Param 1 - Command to execute
    COMMAND="$1"

    #Param 2 - Number of childs in parallel
    MAXCHILD=1
    [[ $# -gt 1 ]] && MAXCHILD="$2"

    #Param 3 - File to be used as FIFO
    FIFO="/tmp/defaultqueue"
    [[ $# -gt 2 ]] && FIFO="$3"

    #Number of seconds to keep the runner active when unused
    TIMEOUT=5

    runner(){
      #Associate file descriptor 3 to the FIFO
      exec 3"$FIFO"

      while read -u 3 -t $TIMEOUT line; do
        #max child check
        while [ `jobs | grep Running | wc -l` -ge "$MAXCHILD" ]; do
          sleep 1
        done

        #exec in backgroud
        (eval "$line")&
      done
      rm $FIFO
    }

    writer(){
      #fork if the runner is not running
      lsof $FIFO >/dev/null || (<*> "QueueRunner" "$MAXCHILD" "$FIFO" &)

      #send the command to the runner
      echo "$COMMAND" > $FIFO
    }

    #Create the FIFO file
    [[ -e "$FIFO" ]] || mkfifo "$FIFO"

    #Start the runner if in the runner fork, else put the command in the queue
    [[ "$COMMAND" == "QueueRunner" ]] && runner || writer

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top