パスワードを忘れた? アカウント作成
6009554 journal
日記

okkyの日記: bashとかでは無理? 19

日記 by okky

今、テキストデータを 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 )ものは無いのだろうか…

# ってこれも結構古典のような気がするんだが…なぜ思い出せない>俺

この議論は、okky (2487)によって ログインユーザだけとして作成されたが、今となっては 新たにコメントを付けることはできません。
  • エレガントじゃないといわれそうだけど、bashでマルチスレッドは(私の知る限り)無理そうなので、2段ロケット式にするのはどうでしょうか。

    1.親bashスクリプトは、80個のファイル名をファイルに出力し(これが作業スタック)、以下の子bashスクリプトを並行作業数だけ(たとえば3個)起動する

    2.子bashスクリプトは以下の仕事をする。

      2-1.作業スタックからファイル名を取得(この時取得したファイル名は消去)してhoge2xlsx.plを起動

      2-2.waitする

      2-3.終わったら作業スタックから次のファイル名を取得(以下ループ)、ファイル名がなくなったらexit

    3.すべての子bashスクリプトが終了したら作業スタックファイルを消去して終了

    --
    人生は七転び八起き、一日は早寝早起き
  • by tslashn (37583) on 2012年08月15日 0時46分 (#2212075)

    そこから、起動するプログラムはPerlで書いてあることから、この環境ではPerlが使えることを仮定してよいはずなので、Perlで書けばよろしいのではないでしょうか。

    fork()が使えないPerlとか、wait()が使えないPerlだったら知らないっと。

  • さいしょに思い浮かんだのは make -j ですが、
    出力がゴチャマゼになるのを嫌って parallel [gnu.org] というものを作った人がいるようです。
    こんなのがあるということは、sh で簡単にできるものではなさそうです。

  •  dodongaです。

     ・ wait %ジョブid か wait プロセスID で待つ
     ・ シグナルを飛ばさせて、trapで待つ。

    --
    閑話休題
    • 1) 3つジョブがあるとして、どれが最初に終わるかがわからないと wait %jobID 方式は使えない。
        つまり、今回はこの方式は意味が無い。

      2) SIGCHLD はほうっておいても飛んでくるし、trap で受け取ることもできる。
       が「どれが終わった」のかはどうやって…あぁ、まて。jobs の出力だと判るか??
       trap で実行している function 中で新しい background job を動かしたとして、それはjob番号何番?

      ふむ… 2番にはまだチャンスがありそう。

      --
      fjの教祖様
      親コメント
      •  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

        --
        閑話休題
        親コメント
        • 試していない以前に、trap というか SIGNAL の意味を勘違いしていると思います。

          --
          fjの教祖様
          親コメント
          •  dodongaです。

             起動関数 を外部シェルスクリプトにして置いてwrapする。
             perlスクリプトが終了したらkillコマンドでSIGNALを飛ばす様にしておく。

             perl に シグナル飛ばさせる関数はないでしたっけか。
             perl 側に 飛ばさせるSIGNALを教えてあげて

             hoge2xlsx.pl ファイル名  signal番号

             とでもしとけば良い気がします。

            # export wppid=$$ 忘れてましたが、要らない?

            #そう言う問題ではない?;;
            #自信なくなってきたぉ;;

            --
            閑話休題
            親コメント
            • そもそも、子プロセスが終了すると SIGCHLD が飛んできますが、「どのプロセスが飛ばしてきたシグナル」なのかは見分けがつきません。
              何個シグナルが飛んできたのかも見分けがつきません(コンテキストスイッチが回ってくる前に子プロセスが2個終了すると、SIGCHLDからは何個子プロセスが終了したのか見分けがつかない。実際この辺に関しては Perl には結構昔から知られているバグがあって、子プロセスが終わったことを取りこぼすことがあり、またそうならないようにするためのコーディングテクニックがある)。

              また、1プロセスに1個づつ、シグナル番号を割り当てられるほど、シグナルは種類がありません(POSIXで Realtime signal を全部かき集めても63個ぐらいしかなくて、ほとんどは使い道が決まっている)。自由になるのは SIGUSR1 と SIGUSR2 ぐらいなもんです。

              というわけで、その方法は根源的にうまくいきません。

              --
              fjの教祖様
              親コメント
  • by ninestars (5792) on 2012年08月15日 12時28分 (#2212379) 日記

    日記の方 [srad.jp]でさくっと書いてみました。
    最近の環境ならばそのまま動くはずです。よろしければどうぞ。

    • ありがたいのですが、それは極めれば極めるほど GNU parallel そのものに…
      というか是非 GNU parallel そのものに…
      # というか オリジナルがもはやどこに行ったのかわからないぐらいライブラリが普及した Expect 同様
      # あらゆる言語で GNU parallel を記述していただいて…

      --
      fjの教祖様
      親コメント
      • by ninestars (5792) on 2012年08月16日 8時36分 (#2212978) 日記

        この手の並列処理での懸念の一つとして、必ずしも全てのジョブが成功するとは限らないという点があります。
        それをどう表現するかという問題もあり、GNU parallel (とリスペクト元のxargs)はEXIT STATUSでジョブが失敗したことは分かるのですが、どれが失敗したのが分からないのが個人的には不満ですね。
        拙作ではベストとは思いませんがそのまま出力してみました。失敗ジョブを抜き出して再実行などの加工がしやすいかと。

        親コメント
  • by sugimoto_t (5022) on 2012年08月16日 8時29分 (#2212976)

    xargs -P とかどうでしょう?

  • でR/Wロック(?)でもかけますか?

    シェルスクリプトでプロセスの完了を確認する方法もある気はしますが、そうするとゾンビ状態の間は時間が無駄になりそうな気もするので...

    --
    M-FalconSky (暑いか寒い)
typodupeerror

長期的な見通しやビジョンはあえて持たないようにしてる -- Linus Torvalds

読み込み中...