programas - scripts bash ejemplos
¿Cómo ejecutar la función dada en Bash en paralelo? (3)
Ha habido algunas preguntas similares, pero mi problema no es "ejecutar varios programas en paralelo", lo que se puede hacer de forma trivial con parallel
o xargs
.
Necesito paralelizar las funciones de Bash.
Imaginemos un código como este:
for i in "${list[@]}"
do
for j in "${other[@]}"
do
# some processing in here - 20-30 lines of almost pure bash
done
done
Parte del procesamiento requiere llamadas a programas externos.
Me gustaría ejecutar algunas (4-10) tareas, cada una de ellas ejecutando diferentes $i
. El número total de elementos en $ list es> 500.
Sé que puedo poner todo el bucle for j ... done
en un script externo, y simplemente llamar a este programa en paralelo, pero ¿es posible hacerlo sin dividir la funcionalidad entre dos programas separados?
Edición: Por favor considere la respuesta de Ole en su lugar.
En lugar de un script separado, puedes poner tu código en una función bash separada. Luego puedes exportarlo y ejecutarlo a través de xargs:
#!/bin/bash
dowork() {
sleep $((RANDOM % 10 + 1))
echo "Processing i=$1, j=$2"
}
export -f dowork
for i in "${list[@]}"
do
for j in "${other[@]}"
do
printf "%s/0%s/0" "$i" "$j"
done
done | xargs -0 -n 2 -P 4 bash -c ''dowork "$@"'' --
Solución para ejecutar comandos multilínea en paralelo:
for ...your_loop...; do
test "$(jobs | wc -l)" -ge 8 && wait -n || true # wait if needed
{
any bash commands here
} &
done
En tu caso:
for i in "${list[@]}"
do
for j in "${other[@]}"
do
test "$(jobs | wc -l)" -ge 8 && wait -n || true
{
your
multi-line
commands
here
} &
done
done
Si hay 8 trabajos de bash ya en ejecución, la wait
esperará a que se complete al menos un trabajo. Si / cuando hay menos trabajos, se inician los nuevos de forma asíncrona.
Beneficios de este enfoque:
- Es muy fácil para comandos multilínea. Todas sus variables se "capturan" automáticamente en el alcance, sin necesidad de pasarlas como argumentos
- Es relativamente rápido. Compare esto, por ejemplo, con el paralelo (estoy citando al
man
oficial):el paralelo es lento en el inicio, aproximadamente 250 ms la primera vez y 150 ms después de eso.
- Solo necesita
bash
para trabajar.
Desventajas:
- Existe la posibilidad de que haya 8 trabajos cuando los contamos, pero menos cuando comenzamos a esperar. (Ocurre si un trabajo termina en esos milisegundos entre los dos comandos). Esto puede hacer que
wait
con menos trabajos que los requeridos. Sin embargo, se reanudará cuando al menos un trabajo se complete, o inmediatamente si hay 0 trabajos en ejecución (en este caso,wait -n
cierra inmediatamente). - Si ya tiene algunos comandos que se ejecutan de forma asíncrona (
&
) dentro del mismo script bash, tendrá menos procesos de trabajo en el bucle.
sem
es parte de GNU Parallel y está hecho para este tipo de situación.
for i in "${list[@]}"
do
for j in "${other[@]}"
do
# some processing in here - 20-30 lines of almost pure bash
sem -j 4 dolong task
done
done
Si le gusta la función, GNU Parallel puede hacer el doble para bucle de una sola vez:
dowork() {
echo "Starting i=$1, j=$2"
sleep 5
echo "Done i=$1, j=$2"
}
export -f dowork
parallel dowork ::: "${list[@]}" ::: "${other[@]}"