web-dev-qa-db-ja.com

wait -nの代わり(サーバーには古いバージョンのbashがあるため)

特定のノードに並列化されたジョブの送信に関する次の問題を解決したいと思います。


問題の構造を説明することから始めましょう

私は2つの非常に単純なMatlabスクリプトを持っています

1)main.m

clear
rng default
P=2;
grid=randn(4,3);
jobs=1;

2)f.m

sgetasknum_grid=grid(jobs*(str2double(getenv('SGE_TASK_ID'))-1)+1: str2double(getenv('SGE_TASK_ID'))*jobs,:); %jobsx3

result=sgetasknum_grid+1; 

filename = sprintf('result.%d.mat', ID);
save(filename, 'result')

exit

私がしたいのは:

  • Main.mを実行します。

  • 次に、f.m 4回を実行し、毎回2つのタスクの並列実行を可能にします。

  • すべてをノードで実行する必要があります[〜#〜] a [〜#〜]


上記の手順を実装する方法は次のとおりです

1)main.mf.mMy_folderという名前のフォルダに保存します

2)以下のようにスクリプトtd.shを作成し、フォルダMy_folderに保存します

#!/bin/bash -l
#$ -S /bin/bash
#$ -l h_vmem=5G
#$ -l tmem=5G
#$ -l h_rt=480:0:0
#$ -cwd
#$ -j y


#$ -N try

date
hostname

J=4 #number tasks

N=2 #number tasks executed in parallel

export SGE_TASK_ID


SGE_TASK_ID=1
n=0
while [ "$SGE_TASK_ID" -le "$J" ]; do
    if [ "$n" -eq "$N" ]; then
        wait -n  # as soon as one task is done, refill it with another
        n=$(( n - 1 ))
    fi

    printf 'Task ID is %d\n' "$SGE_TASK_ID"

    /share/.../matlab -nodisplay -nodesktop -nojvm -nosplash -r "main; ID=$SGE_TASK_ID; f; exit" &

    SGE_TASK_ID=$(( SGE_TASK_ID + 1 ))
    n=$(( n + 1 ))
done

wait

端末に入り、ssh username@A、次にcd /.../My_folder、次にbash td.shと入力します


問題:次のエラーが発生する

td.sh: line 26: wait: -n: invalid option
wait: usage: wait [id]

以下のコメントで気づいたように、問題は@Aのbashのバージョンが古く(4.3で組み込みのwaitに-nオプションが追加された)、sysadminがそれを更新できないことです。可能な最新バージョンはbash4.1です。

したがって、wait -nを置き換える方法を提案できますか?

4
user3285148

Whileループで、待機をまったく使用しないのはどうですか?

while [ "$SGE_TASK_ID" -le "$J" ]; do

    # grep count of matlab processes out of list of user processes
    n = $(ps ux | grep -c "matlab")

    ##  if [ "$n" -le "$N" ]; then
    if [ "$n" -eq "$N" ]; then
        # sleep 1 sec if already max processes started
        sleep 1
        ##  wait -n  # as soon as one task is done, refill it with another
        ##  n=$(( n - 1 ))
    else
        # start another process
        printf 'Task ID is %d\n' "$SGE_TASK_ID"

        /share/.../matlab -nodisplay -nodesktop -nojvm -nosplash -r "main; ID=$SGE_TASK_ID; f; exit" &

        SGE_TASK_ID=$(( SGE_TASK_ID + 1 ))

    fi
    ##  n=$(( n + 1 ))
done

もちろん、grepの文字列は、実行しているものに応じて異なる必要があります(たとえば、give f.mいくつかの特別な名前、およびそのためのgrep。)

1
Jaleks

あなたが書いたそのスクリプトは、gnu parallelで実行するか、-jオプションで作成する方がよいでしょう。または、python(または別の言語)で書き直すこともできます。

見る

  • parallel:bashで使用するためのツール(3つのうち最も簡単で、1つのことだけを実行します)。
  • make:もう少し高度で、独自の言語があります。ファイルの作成に使用されます。例えば_A.b_を作成するには、_A.a_が必要です。_g.f_がある場合は、_z;y;z_を実行します。 _A.a_と_g.f_の作成方法に関するルールを追加することもできます。それは何が何に依存するかを理解し、正しい順序で物事を構築します。可能であれば、並行して処理を実行します(要求された場合)。
  • python:プログラミング言語。スクリプトが実行しようとしていることを実行でき、matlabが実行することを実行できます。

また、これらのうちどれがインストール可能であるかを検討する必要があります。これを行って調べます。

_type parallel
type make
type python
_

注:typeは、入力するための指示ではありません。入力するのはコマンドです。各コマンドのタイプ(ある場合)がわかります。

2
ctrl-alt-delor

質問は、基本的に言う

私がやりたいことは、main.mを実行します。次に、f.mを4回実行し、毎回2つのタスクを並列実行できるようにします。

スクリプトから、f.mに「1」、「2」、「3」、「4」の順次引数を渡してほしいことが明らかになります。私が見る限り、残りは取るに足らない詳細です。

これを行う簡単な方法を次に示します。

main.m
(f.m 1; f.m 3)&
(f.m 2; f.m 4)&
wait

目的のf.mを4回明確に実行し、すべてが完了するのを待ちます。 2番目の複合コマンド(つまり、(f.m 2; f.m 4))は、最初のコマンド(つまり、(f.m 1; f.m 3))が開始した直後に実行を開始するため、2つのプロセスが同時に(並行して)実行されます。また、2つの複合コマンドのそれぞれが一度に1つのf.mプロセスのみを実行するため、最大2つのプロセスが同時に(並行して)実行されます。

これにより、最適なスケジューリングが得られない場合があります。たとえば、f.m 1f.m 3の実行にそれぞれ1分かかり、f.m 2f.m 4の実行にそれぞれ1時間かかる場合、上記の実行には2時間かかります。 —そしてほとんどの場合、実行されるタスクは1つだけです。1時間1分ではなく、ここでの他の多くの試みが実行しようとします。ただし、この質問では最適なスケジュールが指定されておらず、f.mの呼び出しが異なると、時間が大幅に異なるようには見えません。 (繰り返しになりますが、Matlabがわからないため、f.mが何をしているのかわかりません。)


質問は、2と4がパラメーターである一般化されたソリューションを求めていませんが、サンプルコードは一般化されており、他のほとんどの答えもそうであるため、上記の一般化されたバージョンを次に示します。

run_f()
{
        # Usage: run_f start_index step max
        local j

        for ((j=$1; j<=$3; j+=$2))
        do
                f.m "$j"
        done
}

N_PARALLEL=2
TASKS=4

main.m

for ((i=1; i<=$N_PARALLEL; i++))
do
        run_f "$i" "$N_PARALLEL" "$TASKS" &
done

wait

もう少し複雑な例として、N_PARALLELが3であり、TASKSが8であるとします。スクリプトのメインループが実行されます

run_f "1" "3" "8" &
run_f "2" "3" "8" &
run_f "3" "3" "8" &

そして

  • run_f "1" "3" "8"は、f.m 1f.m 4、およびf.m 7を順番に実行します。
  • run_f "2" "3" "8"は、f.m 2f.m 5、およびf.m 8を順番に実行します。
  • run_f "3" "3" "8"f.m 3およびf.m 6を順番に実行します。

自分でインストールできます。

ほとんどのソフトウェアでは、root権限を必要とするインストールプロセスの唯一の部分は、ファイルを標準の場所にコピーすることです。 (デバイスドライバー、カーネル、仮想マシンなどには当てはまりません)

Bashをホームディレクトリにインストールすることをお勧めします(~/bin)スクリプトを使用してそれを使用します。

0
ctrl-alt-delor

シェルスクリプトは、分散リソースマネージャー(おそらくgridengine)用に作成されたように疑わしく見えます。これは、シェルスクリプトを必要とせずに、箱から出してすぐに複数のことを実行することをサポートします。これらの機能を使用してみませんか?

qsub -t 1-4 ./script.sh

次に、SGE_TASK_IDの値を変更するすべての行を削除します。gridengineによって設定されます。

(もちろん、必要に応じて#$-t 1-4のような行を使用して、スクリプトでオプションを設定することもできます...)

0
Wouter Verhelst

GNU並列を使用します。

新しいバージョンには--embedがあり、これはGNU Parallelをシェルスクリプトに埋め込みます。この方法では、GNU Parallelをインストールする必要はありません。クラスター上

したがって、ラップトップに最新バージョンのGNU Parallelをインストールし、次のことを実行します。

$ parallel --embed > myscript.sh

次にmyscript.shを編集します。

#!/bin/bash -l
#$ -S /bin/bash
#$ -l h_vmem=5G
#$ -l tmem=5G
#$ -l h_rt=480:0:0
#$ -cwd
#$ -j y


#$ -N try

# Here starts the original content of myscript.sh
# Embedded GNU Parallel created with --embed
parallel() {
   «This bit removed for brevity (around 13000 lines, generated by gnu parallel)»
   return `cat "$_exit_FILE"; rm "$_exit_FILE"`
}
# Here ends the original content of myscript.sh

date
hostname

J=4 #number tasks
N=2 #number tasks executed in parallel

doit() {
    SGE_TASK_ID="$1"
    printf 'Task ID is %d\n' "$SGE_TASK_ID"

    /share/.../matlab -nodisplay -nodesktop -nojvm -nosplash -r "main; ID=$SGE_TASK_ID; f; exit"
}
export -f doit

seq $J | parallel -j $N doit

最後に、myscript.shtd.shと同じ場所のサーバーにコピーし、td.shを実行するのと同じように実行します。

0
Ole Tange