バッチ処理を並列化する

バッチ処理

並列処理できるように修正したスクリプト達

使用するのはPrint1cycleGraph.shMakeParallel.shtemplate.gpltの3つのファイルとなる。
「MakeParallel.sh」内のパラメータを良いように編集して (「template.gplt」もですね)からコマンドラインで走らせる。

$ bash MakeParallel.sh ⏎

作成する動画ファイルの名前を決めるところで手を抜いているので, 同じ散乱体行列(ディメンジョンについて)で観測する空間の条件が違う(狭いのと広いのとか刻み)計算を並列でさせてはいけない。
動画ファイルの名前がぶつかってしまう(PHIが違えば大丈夫ではあるが)。

できた各PHIの1周期分の動画ファイル群は例えば(L_a x L_b=1.6x1.2, GAMMA=80.0, N_a=N_b=10の散乱体を回しているものであれば)

$ for i in ./Wave-1.6x1.2-g80.0-10x10-???.mp4; do echo "file '$i'" >mylist.txt; done ⏎
$ ffmpeg -f concat -safe 0 -i mylist.txt -c copy Full-Wave-1.6x1.2-g80.0-10x10.mp4 ⏎

とすれば「Full-Wave-1.6x1.2-g80.0-10x10.mp4」という名の動画ファイルにまとめることが出来る。

コンセプト

(散乱体行列のある角度PHIにおける1周期分の波のグラフを描き,パラパラ動画にする)を1つのプロセスとして, 別のPHIについてこれをそれぞれ並列処理できるようにする。

並列処理するためには色々な手法があるが,ここではxargsを用い,-Pオプションで同時実行のプロセス数を指定するやり方とする。

1周期分の命令

シェルスクリプト MakeGraph2.sh は

j=0
endj=90
while [ $j -le $endj ]; do
    PHI=`echo $j \+ 0.0 |bc`
    各種命令
    j=$(($j+1))
done

と変数jを使ってPHIを0度から90度まで回しているが,これを外部から指定する形(xargsが引数として渡す)にする。 gnuplotのコントロールは先と同じ, template.gplt を利用する。

Print1cycleGraph.sh
#!/bin/bash

N_A=$1
N_B=$2
L_A=$3
L_B=$4
GAMMA=$5
PHI=`echo $6 \+ 0.0 |bc`
X_L=$7
X_U=$8
Y_L=$9
Y_U=${10}
T_X=${11}
T_Y=${12}

## 外に書き出すファイル名をプロセス毎に独自のものにさせるためにPIDを使う
LPID=$$

GRAPH=$(printf "%.1fx%.1f-g%.1f-%dx%d-%05.1fdegree" $L_A $L_B $GAMMA $N_A $N_B $PHI)
i=0
endi=19
## 1周期分の波のグラフを作成する
while [ $i -le $endi ]; do
    ## 時間は0.05刻み:1.0で1周期
    NU_T=`echo $i \* 0.05 |bc`
    COMMAND=$(printf "./a.out %f %d %d %f %f %f %f %f %f %f %f %d %d out-%03d-%05.1f-%d" $NU_T $N_A $N_B $L_A $L_B $GAMMA $PHI $X_L $X_U $Y_L $Y_U $T_X $T_Y $i $PHI $LPID)
    $COMMAND
    COMMAND2=$(printf "sed s/OUTFILE/out-%03d-%05.1f-%d/" $i $PHI $LPID)
    COMMAND3=$(printf "sed s/INFILE/out-%03d-%05.1f-%d/g" $i $PHI $LPID)
    COMMAND4=$(printf "sed s/GRAPHTITLE/%s/" "$GRAPH")
    COM=$(printf "COM-%03d-%05.1f-%d.gplt" $i $PHI $LPID)
    cat template.gplt | $COMMAND2 | $COMMAND3 | $COMMAND4 > $COM
    gnuplot < $COM
    rm $COM
    i=$(($i+1))
done

## 1周期分の波のグラフをまとめて動画にする
IPHI=$(echo $PHI |sed 's/\.0*$//')
OUTFILENAME=$(printf "Wave-%.1fx%.1f-g%.1f-%dx%d-%03d.mp4" $L_A $L_B $GAMMA $N_A $N_B $IPHI)
COMMAND0=$(printf "ffmpeg -r 24 -i out-%s-%05.1f-%d.png -an -c:v libx264 -vf format=yuv420p -tune animation" "%03d" $PHI $LPID)
COMMANDV=$(echo "$COMMAND0" $OUTFILENAME)
$COMMANDV
RMFILENAME1=$(printf "out-*-%05.1f-%d.png" $PHI $LPID)
RMFILENAME2=$(printf "out-*-%05.1f-%d*.dat" $PHI $LPID)
rm $RMFILENAME1 $RMFILENAME2

xargsによる命令

ネットで紹介されているxargsの使い方をそのまま真似たので,まあこんなもんです。
計算に使うパラメータを適当に編集して,自分のPCのCPUのスレッド数に見合ったプロセス数を xargs 命令の「-P」の後ろに書き込む。
私のPCは古いのでCPUのスレッド数は8ですが(そのため下の例では7としている),最近のマシンのスレッド数は16とかそれ以上とか。 単スレッドでも速いのにそら激速やわ。

MakeParallel.sh
#!/bin/bash

Cycle_1() {
## 計算に使うパラメータの類いはこのファイルで指定する
## template.gpltのパラメータはこれらに合わせて修正しておくこと
## NU_T はPrint1cycleGraph.shで勝手に生成
    local N_A=15
    local N_B=15
    local L_A=1.6
    local L_B=1.2
    local GAMMA=70.0
## PHI はseqから入力
    local X_L=-30.1
    local X_U=30.1
    local Y_L=-30.1
    local Y_U=30.1
    local T_X=602
    local T_Y=602

    local i=$1
    local ARGUMENTS=$(printf "%d %d %f %f %f %d %f %f %f %f %d %d"  $N_A $N_B $L_A $L_B $GAMMA $i $X_L $X_U $Y_L $Y_U $T_X $T_Y)
    echo "Task $i: Started (on PID $$)"
    bash Print1cycleGraph.sh $ARGUMENTS
    echo "Task $i: Finished"
}

export -f Cycle_1

## seq に与えた数字がxargsへ:seq 0 180 で 0, 1, 2, ..., 180が順に送られる → PHIが0°から180°まで1°刻み
## -P オプションで一度に作成されるプロセス数を指定。{(CPUが一度に処理できるスレッド数)-1}くらいを入れておけば良い
seq 0 180 |xargs -P 7 -I {} bash -c 'Cycle_1 "$@"' _ {}