Executando comandos em paralelo com um limite de número simultâneo de comandos


23

Sequencial: for i in {1..1000}; do do_something $i; done- muito lento

Paralelo: for i in {1..1000}; do do_something $i& done- muita carga

Como executar comandos em paralelo, mas não mais do que, por exemplo, 20 instâncias por momento?

Agora geralmente usando o hack like for i in {1..1000}; do do_something $i& sleep 5; done, mas essa não é uma boa solução.

Atualização 2 : converteu a resposta aceita em um script: http://vi-server.org/vi/parallel

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Observe que você deve substituir 8 espaços por 2 guias antes de "i =" para fazê-lo funcionar.

Respostas:


15

Paralelo GNU é feito para isso.

seq 1 1000 | parallel -j20 do_something

Pode até executar trabalhos em computadores remotos. Aqui está um exemplo para recodificar um MP3 para OGG usando o server2 e o computador local executando 1 trabalho por núcleo de CPU:

parallel --trc {.}.ogg -j+0 -S server2,: \
     'mpg321 -w - {} | oggenc -q0 - -o {.}.ogg' ::: *.mp3

Assista a um vídeo de introdução ao GNU Parallel aqui:

http://www.youtube.com/watch?v=OpaiGYxkSuQ


Ainda não sei sobre "moreutils" e que já existe uma ferramenta para o trabalho. Olhando e comparando.
— Vi.

1
O parallelin moreutils não é GNU Parallel e é bastante limitado em suas opções. O comando acima não será executado com o paralelo de moreutils.
— precisa saber é o seguinte

1
Mais uma opção: xargs --max-procs=20.
— Vi.

4

Não é uma solução bash, mas você deve usar um Makefile, possivelmente -lpara não exceder uma carga máxima.

NJOBS=1000

.PHONY = jobs
jobs = $(shell echo {1..$(NJOBS)})

all: $(jobs)

$(jobs):
    do_something $@

Então, para iniciar 20 trabalhos por vez, faça

$ make -j20

ou iniciar o maior número possível de tarefas sem exceder uma carga de 5

$ make -j -l5

Parece que a solução não hacky por enquanto.
— Vi.

2
echo -e 'PHONY=jobs\njobs=$(shell echo {1..100000})\n\nall: ${jobs}\n\n${jobs}:\n\t\techo $@; sleep `echo $$RANDOM/6553 | bc -l`' | make -f - -j20Agora parece mais hacky novamente.
— Vi.

@vi: oh meu ....
— Benjamin Bannier

Convertido sua solução em um script. Agora pode ser usado com facilidade.
— Vi.

2

postando o script na pergunta com formatação:

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Observe que você deve substituir 8 espaços por 2 guias antes de "i =".


1

Uma ideia simples:

Verifique o módulo 20 e execute o comando shell de espera antes de fazer alguma coisa.


Ele aguardará a conclusão de todas as tarefas atuais (criando afundamentos no gráfico de número de tarefas) ou aguardará uma tarefa específica que possa parar por mais tempo (novamente criando afundamentos nesse caso)
— Vi.

@ Vi: Shell wait é para todas as tarefas em segundo plano que pertencem a esse shell.
— harrymc

1

Você pode usar pspara contar quantos processos você está executando e, sempre que isso cai abaixo de um determinado limite, você inicia outro processo.

Pseudo-código:

i = 1
MAX_PROCESSES=20
NUM_TASKS=1000
do
  get num_processes using ps
  if num_processes < MAX_PROCESSES
    start process $i
    $i = $i + 1
  endif
  sleep 1 # add this to prevent thrashing with ps
until $i > NUM_TASKS

1
for i in {1..1000}; do 
     (echo $i ; sleep `expr $RANDOM % 5` ) &
     while [ `jobs | wc -l` -ge 20 ] ; do 
         sleep 1 
     done
done

Pode ser while [ `jobs | wc -l` -ge 20]; do?
— Vi.

certo, mas na minha amostra, eu então tem que calcular njobsduas vezes, eo desempenho é muito importante em scripts shell que as tarefas de execução do sono;)
— msw

Quero dizer, sua versão não funciona conforme o esperado. Eu mudo sleep 1para sleep 0.1e ele começa a média de njobs para 40-50 em vez de 20. Se houver mais de 20 trabalhos, precisamos esperar que qualquer trabalho seja concluído, e não apenas esperar 1 segundo.
— Vi.

0

você pode fazer assim.

threads=20
tempfifo=$PMS_HOME/$$.fifo

trap "exec 1000>&-;exec 1000<&-;exit 0" 2
mkfifo $tempfifo
exec 1000<>$tempfifo
rm -rf $tempfifo

for ((i=1; i<=$threads; i++))
do
    echo >&1000
done

for ((j=1; j<=1000; j++))
do
    read -u1000
    {
        echo $j
        echo >&1000
    } &
done

wait
echo "done!!!!!!!!!!"

usando pipes nomeados, sempre executa 20 subcascas em paralelo.

Espero que ajude :)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.