okkyの日記: bashとかでは無理? 19
今、テキストデータを Excel 化する Perl スクリプトを動かしているのだが…これが時間がかかる。
今の処270分(シリアライズした場合)かかっており、今後も伸びることが予測される。
で、このPerlスクリプト、CPU coreを1つしか消費しない。また、処理対象は80個ある。
なので、プロセスを並列で起動してやれば並列で処理できる。ただし、coreは8個しかないし、メモリ的に8並列はつらい。
そこで並列数を予め教えておくと、その数までプロセスを起動して終わったら次…とするようなスクリプトを書いた。
declare -i count=0
declare -i maxparallel=3
for i in *fo; do
count=$count+1
echo $count;
cat ${i} | hoge2xlsx.pl ${i}.xlsx &
if (( $count >= $maxparallel )); then
wait
count=0
fi
done
wait
まぁ、これでも大雑把にはうまくいくのだが…これは上記の例の場合だと3つプロセスを動かして、3つ全部終わらないと次の3つを実行しない。これはやりたいこととはちょっと違う。
やりたいのは「3つ並列で動かして、どれか1つ終わったら、次の1つを実行する」ということなのだが…。
C言語でいう wait(2) に相当する(つまり、「だれかが終わる」まで待つのであって「全部が終わる」まで待つのではない wait )ものは無いのだろうか…
# ってこれも結構古典のような気がするんだが…なぜ思い出せない>俺
bashスクリプトを2段式にする (スコア:2)
エレガントじゃないといわれそうだけど、bashでマルチスレッドは(私の知る限り)無理そうなので、2段ロケット式にするのはどうでしょうか。
1.親bashスクリプトは、80個のファイル名をファイルに出力し(これが作業スタック)、以下の子bashスクリプトを並行作業数だけ(たとえば3個)起動する
2.子bashスクリプトは以下の仕事をする。
2-1.作業スタックからファイル名を取得(この時取得したファイル名は消去)してhoge2xlsx.plを起動
2-2.waitする
2-3.終わったら作業スタックから次のファイル名を取得(以下ループ)、ファイル名がなくなったらexit
3.すべての子bashスクリプトが終了したら作業スタックファイルを消去して終了
人生は七転び八起き、一日は早寝早起き
Re:bashスクリプトを2段式にする (スコア:1)
作業スタックファイルの操作の処に排他制御が必要そうです。
fjの教祖様
Re:bashスクリプトを2段式にする (スコア:1)
1ジョブ=1スレッドを割り当てて制御する、というコンセプトで
少し大げさな仕掛けになりますが、Xcryptというものを作っておられる方がいらっしゃります。
http://super.para.media.kyoto-u.ac.jp/xcrypt/index.html [kyoto-u.ac.jp]
Perl (スコア:2)
そこから、起動するプログラムはPerlで書いてあることから、この環境ではPerlが使えることを仮定してよいはずなので、Perlで書けばよろしいのではないでしょうか。
fork()が使えないPerlとか、wait()が使えないPerlだったら知らないっと。
make -j (スコア:1)
さいしょに思い浮かんだのは make -j ですが、
出力がゴチャマゼになるのを嫌って parallel [gnu.org] というものを作った人がいるようです。
こんなのがあるということは、sh で簡単にできるものではなさそうです。
Re:make -j (スコア:1)
ここ [riywo.com]に貼られている動画とかを見ると、
GNU parallel 変態っ!!(いい意味で)と思う。
が、とりあえず make -j でやってみます(あれ?)
fjの教祖様
ぱっと見で思いついたこと (スコア:1)
dodongaです。
・ wait %ジョブid か wait プロセスID で待つ
・ シグナルを飛ばさせて、trapで待つ。
閑話休題
どちらもNG (スコア:1)
1) 3つジョブがあるとして、どれが最初に終わるかがわからないと wait %jobID 方式は使えない。
つまり、今回はこの方式は意味が無い。
2) SIGCHLD はほうっておいても飛んでくるし、trap で受け取ることもできる。
が「どれが終わった」のかはどうやって…あぁ、まて。jobs の出力だと判るか??
trap で実行している function 中で新しい background job を動かしたとして、それはjob番号何番?
ふむ… 2番にはまだチャンスがありそう。
fjの教祖様
試してないけど(^^; (スコア:1)
dodongaです。
試してません。
# コマンド名 ファイル名 [ファイル名 ・・・]
wpid1=0
wpid13=0
wpid15=0
arvc = $#
trap "kickoff1" 1
trap "kickoff13" 13
trap "kickoff15" 15
function kickoff11()
{
if [ argc -ne 0 ] then
wait $wpid1
起動関数1 $1
shift
wpid1 = $!
argc = $argc - 1;
fi
}
・
・
・
# ココまで定義 -----------------
起動関数1 $1
wpid1 = $!
shift
起動関数13 $1
wpid13 = $!
shift
起動関数15 $1
wpid15 = $!
shift
while((1))
do
if [ argc -le 0 ] then
wait
fi
sleep(1)
done
閑話休題
Re:試してないけど(^^; (スコア:1)
試していない以前に、trap というか SIGNAL の意味を勘違いしていると思います。
fjの教祖様
起動関数に引数でなんのシグナルを飛ばされるかを指定させる? (スコア:1)
dodongaです。
起動関数 を外部シェルスクリプトにして置いてwrapする。
perlスクリプトが終了したらkillコマンドでSIGNALを飛ばす様にしておく。
perl に シグナル飛ばさせる関数はないでしたっけか。
perl 側に 飛ばさせるSIGNALを教えてあげて
hoge2xlsx.pl ファイル名 signal番号
とでもしとけば良い気がします。
# export wppid=$$ 忘れてましたが、要らない?
#そう言う問題ではない?;;
#自信なくなってきたぉ;;
閑話休題
Re:起動関数に引数でなんのシグナルを飛ばされるかを指定させる? (スコア:1)
そもそも、子プロセスが終了すると SIGCHLD が飛んできますが、「どのプロセスが飛ばしてきたシグナル」なのかは見分けがつきません。
何個シグナルが飛んできたのかも見分けがつきません(コンテキストスイッチが回ってくる前に子プロセスが2個終了すると、SIGCHLDからは何個子プロセスが終了したのか見分けがつかない。実際この辺に関しては Perl には結構昔から知られているバグがあって、子プロセスが終わったことを取りこぼすことがあり、またそうならないようにするためのコーディングテクニックがある)。
また、1プロセスに1個づつ、シグナル番号を割り当てられるほど、シグナルは種類がありません(POSIXで Realtime signal を全部かき集めても63個ぐらいしかなくて、ほとんどは使い道が決まっている)。自由になるのは SIGUSR1 と SIGUSR2 ぐらいなもんです。
というわけで、その方法は根源的にうまくいきません。
fjの教祖様
pythonだけど (スコア:1)
日記の方 [srad.jp]でさくっと書いてみました。
最近の環境ならばそのまま動くはずです。よろしければどうぞ。
Re:pythonだけど (スコア:1)
ありがたいのですが、それは極めれば極めるほど GNU parallel そのものに…
というか是非 GNU parallel そのものに…
# というか オリジナルがもはやどこに行ったのかわからないぐらいライブラリが普及した Expect 同様
# あらゆる言語で GNU parallel を記述していただいて…
fjの教祖様
Re:pythonだけど (スコア:1)
この手の並列処理での懸念の一つとして、必ずしも全てのジョブが成功するとは限らないという点があります。
それをどう表現するかという問題もあり、GNU parallel (とリスペクト元のxargs)はEXIT STATUSでジョブが失敗したことは分かるのですが、どれが失敗したのが分からないのが個人的には不満ですね。
拙作ではベストとは思いませんがそのまま出力してみました。失敗ジョブを抜き出して再実行などの加工がしやすいかと。
実用 (スコア:2)
実用に供されている、つまりそこそこ以上テストされている、
スクリプトを適当なProjectから引っ張る、たとえば
http://trinityrnaseq.svn.sourceforge.net/viewvc/trinityrnaseq/trunk/ut... [sourceforge.net]
なんかどうでしょう。すぐに読める長さ。
ライセンスはご確認ください。
http://trinityrnaseq.svn.sourceforge.net/viewvc/trinityrnaseq/trunk/LICENSE [sourceforge.net]
Re:pythonだけど (スコア:1)
ある処理が成功しなかったとして、それ以降の処理を停止するべきか、続行するべきか、というのがポイントでしょう。
GNU parallel のようなソフトは
「いやいや、続行するべきでしょ? どれが失敗したのかは自分で記録してよ。
つーか、あとで調べて分かるなら、記録する必要すらねーべ?!?」
というスタンスなのではないかと。個人的には
「完全に独立した処理をひたすら並行処理するだけ」
の機構は、そちらのほうがありがたいです。
fjの教祖様
xargs (スコア:1)
xargs -P とかどうでしょう?
テンポラリファイル (スコア:0)
でR/Wロック(?)でもかけますか?
シェルスクリプトでプロセスの完了を確認する方法もある気はしますが、そうするとゾンビ状態の間は時間が無駄になりそうな気もするので...
M-FalconSky (暑いか寒い)