Quatro tarefas em paralelo… como faço isso?

23

Eu tenho um monte de imagens PNG em um diretório. Eu tenho um aplicativo chamado pngout que eu corro para compactar essas imagens. Este aplicativo é chamado por um script que fiz. O problema é que esse script faz um de cada vez, algo assim:

FILES=(./*.png)
for f in  "${FILES[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 $f R${f/\.\//}
done

Processar apenas um arquivo de cada vez, leva muito tempo. Depois de executar este aplicativo, vejo que a CPU é de apenas 10%. Então eu descobri que eu posso dividir esses arquivos em 4 lotes, colocar cada lote em um diretório e disparar 4, a partir de quatro janelas de terminal, quatro processos, então eu tenho quatro instâncias do meu script, ao mesmo tempo, processando essas imagens e trabalho leva 1/4 do tempo.

O segundo problema é que perdi tempo dividindo as imagens e os lotes e copiando o script para quatro diretórios, abrindo 4 janelas de terminal, bla bla ...

Como fazer isso com um script, sem ter que dividir nada?

Quero dizer duas coisas: primeiro, como faço de um script bash, disparar um processo para o segundo plano? (basta adicionar & ao final?) Segundo: como eu paro de enviar tarefas para o segundo plano depois de enviar as quarta tarefas e coloco o script para esperar até que as tarefas terminem? Quero dizer, apenas enviando uma nova tarefa para o segundo plano quando uma tarefa termina, mantendo sempre 4 tarefas em paralelo? se eu não fizer isso, o loop disparará zilhões de tarefas para o segundo plano e a CPU ficará obstruída.

    
por SpaceDog 31.03.2012 / 05:57

4 respostas

33

Se você tiver uma cópia de xargs que suporta execução paralela com -P , basta fazer

printf '%s
printf '%s%pre%' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}
' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}

Para outras ideias, o wiki do Wooledge Bash tem uma seção no artigo Gerenciamento de processos descrevendo exatamente o que você quer.

    
por 31.03.2012 / 06:05
8

Além das soluções já propostas, você pode criar um makefile que descreva como fazer um arquivo compactado de descompactado e use make -j 4 para executar 4 tarefas em paralelo. O problema é que você precisará nomear os arquivos compactados e descomprimidos de maneira diferente, ou armazená-los em diretórios diferentes, caso contrário, escrever uma regra de criação razoável será impossível.

    
por 31.03.2012 / 06:45
7

Se você tem o link do GNU Paralelo instalado, você pode fazer isso:

parallel ./pngout -s0 {} R{} ::: *.png

Você pode instalar o GNU Parallel simplesmente por:

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

Assista aos vídeos de introdução do GNU Parallel para saber mais: link

    
por 05.04.2012 / 21:05
5

Para responder às suas duas perguntas:

  • sim, adicionando & no final da linha, você instruirá o shell a iniciar um processo em segundo plano.
  • usando o comando wait , você pode solicitar ao shell que espere que todos os processos em segundo plano sejam concluídos antes de continuar.

Aqui está o script modificado para que j seja usado para acompanhar o número de processos em segundo plano. Quando NB_CONCURRENT_PROCESSES for atingido, o script redefinirá j para 0 e aguardará a conclusão de todos os processos em segundo plano antes de retomar sua execução.

files=(./*.png)
nb_concurrent_processes=4
j=0
for f in "${files[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 "$f" R"${f/\.\//}" &
        ((++j == nb_concurrent_processes)) && { j=0; wait; }
done
    
por 31.03.2012 / 07:42