GNU paralelo com loop for?

3

Encontrei respostas próximas a isso, mas não consigo entender como usá-las no meu caso (sou novo no Bash) ... então, estou tentando processar uma pasta que contém uma seqüência de imagens grande ( 100k +) com o Imagemagick e gostaria de usar o GNU Parallel para acelerar as coisas.

Este é o código que eu uso (processando 100 quadros de cada vez para evitar ficar sem memória ram):

calcmethod1=mean;
allframes=(*.png)
cd out1

for (( i=0; i < "${#allframes[@]}" ; i+=100 )); do 
    convert "${allframes[@]:i:100}" -evaluate-sequence "$calcmethod1" \
        -channel RGB -normalize ../out2/"${allframes[i]}"
done

como eu 'paralelizaria' isso? A maioria das soluções que eu encontrei funciona com o não uso de um loop, mas a tubulação - mas fazendo isso eu encontrei o problema que meu script iria quebrar por causa da minha lista de argumentos ficando muito longa ...

Eu acho que o que eu gostaria de ter é parallel dividindo a carga, como passar os primeiros 100 quadros para o núcleo 1, os quadros 100-199 para o núcleo 2, etc.?

    
por user3647558 24.07.2018 / 20:53

3 respostas

3

Encomendar

Seu programa de amostra não pareceu se importar com a ordem do *.png para a matriz allframes que você estava construindo, mas seus comentários levaram-me a acreditar que a ordem importaria.

I guess what I would want to do is to have parallel splitting the load like handing the first 100 frames to core 1, frames 100-199 to core 2 etc.?

Bash

Portanto, eu começaria com uma modificação em seu script, alterando a construção da matriz allframes para que os arquivos sejam armazenados em ordem numérica.

allframes=($(printf "%s\n" *.png | sort -V | tr '\n' ' '))

Isso pode ser simplificado ainda mais usando sort -zV :

allframes=($(printf "%s
$ convert "0.png 1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png \
          10.png 11.png 12.png 13.png 14.png 15.png 16.png 17.png 18.png \
          19.png 20.png 21.png 22.png 23.png 24.png 25.png 26.png 27.png \
          28.png 29.png 30.png 31.png 32.png 33.png 34.png 35.png 36.png \
          37.png 38.png 39.png 40.png 41.png 42.png 43.png 44.png 45.png \
          46.png 47.png 48.png 49.png 50.png 51.png 52.png 53.png 54.png \
          55.png 56.png 57.png 58.png 59.png 60.png 61.png 62.png 63.png \
          64.png 65.png 66.png 67.png 68.png 69.png 70.png 71.png 72.png \
          73.png 74.png 75.png 76.png 77.png 78.png 79.png 80.png 81.png \
          82.png 83.png 84.png 85.png 86.png 87.png 88.png 89.png 90.png \
          91.png 92.png 93.png 94.png 95.png 96.png 97.png 98.png 99.png" \
          -evaluate-sequence "mean" -channel RGB -normalize ../out2/0.png
" *.png | sort -zV | tr '
$ printf '%s\n' *.png | sort -V | parallel -n100 --dryrun convert {} \
   -evaluate-sequence 'mean' -channel RGB -normalize ../out2/{1}
' ' '))

Isso tem o efeito de construir seus comandos convert ... para que eles fiquem assim agora:

$ printf '%s
$ convert 0.png 1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png 10.png \
         11.png 12.png 13.png 14.png 15.png 16.png 17.png 18.png 19.png \
         20.png 21.png 22.png 23.png 24.png 25.png 26.png 27.png 28.png \
         29.png 30.png 31.png 32.png 33.png 34.png 35.png 36.png 37.png \
         38.png 39.png 40.png 41.png 42.png 43.png 44.png 45.png 46.png \
         47.png 48.png 49.png 50.png 51.png 52.png 53.png 54.png 55.png \ 
         56.png 57.png 58.png 59.png 60.png 61.png 62.png 63.png 64.png \ 
         65.png 66.png 67.png 68.png 69.png 70.png 71.png 72.png 73.png \ 
         74.png 75.png 76.png 77.png 78.png 79.png 80.png 81.png 82.png \
         83.png 84.png 85.png 86.png 87.png 88.png 89.png 90.png 91.png \
         92.png 93.png 94.png 95.png 96.png 97.png 98.png 99.png \
         -evaluate-sequence mean -channel RGB -normalize ../out2/0.png
' *.png | sort -zV | parallel -0 -n100 --dryrun "convert {} \ -evaluate-sequence 'mean' -channel RGB -normalize ../out2/{1}

Parallels

Criando o exemplo de eschwartz, eu juntei um exemplo de parallel da seguinte forma:

$ printf '%s
allframes=($(printf "%s\n" *.png | sort -V | tr '\n' ' '))
' *.png | sort -zV | parallel -0 -n100 convert {} \ -evaluate-sequence 'mean' -channel RGB -normalize

novamente, mais simplesmente usando sort -zV :

allframes=($(printf "%s
$ convert "0.png 1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png \
          10.png 11.png 12.png 13.png 14.png 15.png 16.png 17.png 18.png \
          19.png 20.png 21.png 22.png 23.png 24.png 25.png 26.png 27.png \
          28.png 29.png 30.png 31.png 32.png 33.png 34.png 35.png 36.png \
          37.png 38.png 39.png 40.png 41.png 42.png 43.png 44.png 45.png \
          46.png 47.png 48.png 49.png 50.png 51.png 52.png 53.png 54.png \
          55.png 56.png 57.png 58.png 59.png 60.png 61.png 62.png 63.png \
          64.png 65.png 66.png 67.png 68.png 69.png 70.png 71.png 72.png \
          73.png 74.png 75.png 76.png 77.png 78.png 79.png 80.png 81.png \
          82.png 83.png 84.png 85.png 86.png 87.png 88.png 89.png 90.png \
          91.png 92.png 93.png 94.png 95.png 96.png 97.png 98.png 99.png" \
          -evaluate-sequence "mean" -channel RGB -normalize ../out2/0.png
" *.png | sort -zV | tr '
$ printf '%s\n' *.png | sort -V | parallel -n100 --dryrun convert {} \
   -evaluate-sequence 'mean' -channel RGB -normalize ../out2/{1}
' ' '))

OBSERVAÇÃO: O acima tem um eco "..." como a ação parallel para iniciar. Fazer isso dessa maneira ajuda a visualizar o que está acontecendo:

$ printf '%s
$ convert 0.png 1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png 10.png \
         11.png 12.png 13.png 14.png 15.png 16.png 17.png 18.png 19.png \
         20.png 21.png 22.png 23.png 24.png 25.png 26.png 27.png 28.png \
         29.png 30.png 31.png 32.png 33.png 34.png 35.png 36.png 37.png \
         38.png 39.png 40.png 41.png 42.png 43.png 44.png 45.png 46.png \
         47.png 48.png 49.png 50.png 51.png 52.png 53.png 54.png 55.png \ 
         56.png 57.png 58.png 59.png 60.png 61.png 62.png 63.png 64.png \ 
         65.png 66.png 67.png 68.png 69.png 70.png 71.png 72.png 73.png \ 
         74.png 75.png 76.png 77.png 78.png 79.png 80.png 81.png 82.png \
         83.png 84.png 85.png 86.png 87.png 88.png 89.png 90.png 91.png \
         92.png 93.png 94.png 95.png 96.png 97.png 98.png 99.png \
         -evaluate-sequence mean -channel RGB -normalize ../out2/0.png
' *.png | sort -zV | parallel -0 -n100 --dryrun "convert {} \ -evaluate-sequence 'mean' -channel RGB -normalize ../out2/{1}

Se você estiver satisfeito com essa saída, basta remover a opção --dryrun para parallel e executá-la novamente.

$ printf '%s%pre%' *.png | sort -zV | parallel -0 -n100 convert {} \ 
    -evaluate-sequence 'mean' -channel RGB -normalize

Referências

por 25.07.2018 / 09:23
3

A solução correta é imprimir os nomes dos arquivos usando um shell embutido como printf '%sparallel --null' *.png , que não é vulnerável à limitação ARG_MAX do comprimento do argumento de linha de comando, e então canalizá-lo para parallel , que lerá esses nomes de arquivos empregos como quiser.

Alguns recursos de --null que usaremos:

  • -n 100 é necessário para dividir os nomes dos arquivos em caracteres nulos para evitar problemas estranhos com nomes de arquivos estranhos
  • {} irá, assim como xargs, manipular 100 arquivos para cada chamada
  • ../out2/{1} contém esses 100 nomes de arquivos
  • parallel contém apenas o primeiro

Então, isso se tornaria:

calcmethod1=mean
printf '%s
calcmethod1=mean
printf '%s%pre%' *.png | parallel --null -n 100 convert {} -evaluate-sequence $calcmethod1 -channel RGB -normalize {} ../out2/{1}
' *.png | parallel --null -n 100 convert {} -evaluate-sequence $calcmethod1 -channel RGB -normalize {} ../out2/{1}

Por que você acha que a tubulação não funcionaria? O piping funciona bem, são apenas comandos bifurcados externamente que não lêem a partir de um pipe, que têm problemas com o tamanho do argumento . Piping é, na verdade, todo o propósito de %code% .

    
por 24.07.2018 / 21:40
2

É possível executar todos os processos convert em sua própria sub-lista:

#!/bin/bash

for (( i=1; i<=1000; i++ )) do
(
command --options ) &
disown
done

exit 0

Para ver como funciona, tente este script:

#!/bin/bash

echo "Hi!"

for (( i=1; i<=1000; i++ )) do
(
sleep 30
echo "Bye, "$i"!" ) &
disown
done

exit 0

Atribua ao nome do script par.sh e verifique os processos depois:

ps aux | grep par.sh

Podemos supor que o balanceador de carga da CPU nativa do Linux deve distribuir os processos entre os núcleos da CPU uniformemente, pois cada subshell tem um pid separado. De qualquer forma, algo como cpuset está sempre disponível para ser usado.

    
por 24.07.2018 / 23:40