web-dev-qa-db-ja.com

GNU forループと並列?

私はこれに近い答えを見つけましたが、私の場合、それらの使用方法を理解できませんでした(私はBashの初心者です)...したがって、大きな画像シーケンス(100k以上のファイル)を含むフォルダーを処理しようとしていますImagemagickで、GNU Parallelを使用して高速化したいと考えています。

これは私が使用するコードです(RAMが不足するのを避けるために一度に100フレームを処理します):

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

これをどのように「並列化」しますか?私が見つけたほとんどの解決策はループではなくパイプを使用して動作します-しかし、これを行うと、引数リストが長くなりすぎてスクリプトが壊れるという問題に遭遇しました...

最初の100フレームをコア1に、フレーム100〜199をコア2に渡すように、負荷をparallelで分割することになると思いますか?

3
user3647558

注文

あなたのサンプルプログラムはあなたが構築しているallframes配列の*.pngの順序を気にしていないようでしたが、あなたのコメントは私にその順序が重要であると信じさせました。

最初の100フレームをコア1に、フレーム100〜199をコア2に渡すように、負荷を並列に分割することをお勧めします。

Bash

したがって、スクリプトを変更して、allframes配列の構成を変更し、ファイルが数値順に格納されるようにすることから始めます。

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

これは、sort -zVを使用してさらに簡略化できます。

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

これはconvert ...コマンドの作成に影響を与え、次のようになります。

$ 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

Parallels

Eschwartzの例を基にして、次のようにparallelの例をまとめます。

$ printf '%s\n' *.png | sort -V | parallel -n100 --dryrun convert {} \
   -evaluate-sequence 'mean' -channel RGB -normalize ../out2/{1}

もう一度、より単純にsort -zVを使用します。

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

注:上記には、開始するparallelアクションとしてエコー「...」があります。このようにすると、何が起こっているのかを視覚化するのに役立ちます。

$ 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

この出力で問題がなければ、--dryrunスイッチをparallelに削除して、再実行してください。

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

参考文献

3
slm

正しい解決策は、コマンドライン引数の長さのARG_MAX制限に対して脆弱ではないprintf '%s\0' *.pngのようなシェル組み込みを使用してファイル名を出力し、それをparallel --nullにパイプして、それらのファイル名とバッチを読み取ることです。あなたが望む仕事。

使用するparallelのいくつかの機能:

  • --nullは、奇妙なファイル名の奇妙な問題を防ぐために、ヌル文字でファイル名を正しく分割する必要があります
  • -n 100は、xargsと同様に、呼び出しごとに100ファイルを処理します
  • {}には100個のファイル名が含まれています
  • ../out2/{1}には最初のものが含まれています

したがって、これは次のようになります。

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

なぜ配管が機能しないと思いますか?パイピングは正常に機能します。パイプからnotを読み取るのは外部的にフォークされたコマンドのみであり、これは引数の長さに問題があります。実際、パイピングはparallelの目的です。

3
eschwartz

すべてのconvertプロセスを独自のサブシェルで実行することが可能です。

#!/bin/bash

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

exit 0

動作を確認するには、次のスクリプトを試してください。

#!/bin/bash

echo "Hi!"

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

exit 0

スクリプト名に割り当てるpar.shおよびその後のプロセスを確認します。

ps aux | grep par.sh

すべてのサブシェルに個別のpidがあるため、ネイティブLinux CPUロードバランサーはCPUコア間でプロセスを均等に分散する必要があると想定できます。とにかく、cpusetのようなものがいつでも使用できます。

2
Bob