2011-07-06 4 views
21

私はいくつかの重い処理を行う必要のある大きなファイル群を持っています。 このシングルスレッド処理では、数百MiBのRAM(ジョブの開始に使用されたマシン上)が使用され、実行には数分かかります。 私の現在のusecaseは、入力データに対してhadoopジョブを開始することですが、これまでにも同じ問題がありました。限られた数の子プロセスをbashで並列に実行していますか?

利用可能なCPUパワーを完全に利用するために、私はパラレルで複数のタスクを実行できるようにしたいと考えています。

は、しかし、このような非常に簡単なシェルスクリプトの例は、過剰な負荷やスワッピングにシステムパフォーマンスをゴミ箱ます:
find . -type f | while read name ; 
do 
    some_heavy_processing_command ${name} & 
done 

だから私が欲しいもの「にgmake -j4」何に本質的に類似しています。

bashは "wait"コマンドをサポートしていますが、すべての子プロセスが完了するまで待機することがわかります。過去に私は 'ps'コマンドを実行し、子プロセスを名前でgrepするスクリプトを作成しました(はい、私は知っています...醜いです)。

私がしたいことをするための最も簡単できれいな解決策は何ですか?


編集:フレデリックのおかげ:はい確かにこれはHow to limit number of threads/sub-processes used in a function in bash の複製である "xargsの--max-procsの= 4" は、魔法のように動作します。 (だから私は自分の質問を閉じるために投票)

+8

私は 'xargsのを使用したいhttp://stackoverflow.com/questions/6511884/how-to-limit-number-of-threads-used-in-a-function-in-bashの可能重複--max-procs = 4'これに対して... –

+4

それは[GNU並列](http://www.gnu.org/software/parallel/)の仕事のようですが、余分なものが追加されているのか分かりません私が知らなかった 'xargs --max-procs'への力。 – larsen

+0

@Niels:私は目的のために' screen'を使っていましたが、これはちょっと面倒ですが、特に別の 'screen'セッション); – 0xC0000022L

答えて

18
#! /usr/bin/env bash 

set -o monitor 
# means: run background processes in a separate processes... 
trap add_next_job CHLD 
# execute add_next_job when we receive a child complete signal 

todo_array=($(find . -type f)) # places output into an array 

index=0 
max_jobs=2 

function add_next_job { 
    # if still jobs to do then add one 
    if [[ $index -lt ${#todo_array[*]} ]] 
    # apparently stackoverflow doesn't like bash syntax 
    # the hash in the if is not a comment - rather it's bash awkward way of getting its length 
    then 
     echo adding job ${todo_array[$index]} 
     do_job ${todo_array[$index]} & 
     # replace the line above with the command you want 
     index=$(($index+1)) 
    fi 
} 

function do_job { 
    echo "starting job $1" 
    sleep 2 
} 

# add initial set of jobs 
while [[ $index -lt $max_jobs ]] 
do 
    add_next_job 
done 

# wait for all jobs to complete 
wait 
echo "done" 

は、このコードは私のために非常によく働いた

+0

私は今コードを理解していますが、ちょっと考えなければなりません。特に、これらが並行して(なぜなら、サブプロセスであるため)実行される理由については、私には分かりませんでした。私はそれがコードにその部分のコメントを追加する価値があると思います。 – 0xC0000022L

+0

私の現在のアプリケーションはxargs --max-procsでうまく動作しますが、あなたのスクリプトはより多くの状況で使えるので、私はまだ "答え"であるという信用を与えています。ありがとう。 –

3

...フレドリックはxargsのは、正確に何をしたいん優れたポイントを作ることを言いました。

スクリプトが終了できなかった1つの問題に気付きました。 max_jobsが配列の要素数よりも多いためにスクリプトが終了しないケースが発生した場合、スクリプトは決して終了しません。

上記のシナリオを防ぐため、「max_jobs」宣言の後に次のように追加しました。

if [ $max_jobs -gt ${#todo_array[*]} ]; 
    then 
      # there are more elements found in the array than max jobs, setting max jobs to #of array elements" 
      max_jobs=${#todo_array[*]} 
fi 
20

私はこの答えをパーティーに遅刻を知っているが、私は私見、スクリプトクリーンで簡単なのボディを作る、という選択肢を投稿するだろうと思いました。 (。明らかに、あなたのシナリオのために適切であるように値2 & 5を変更することができます)

function max2 { 
    while [ `jobs | wc -l` -ge 2 ] 
    do 
     sleep 5 
    done 
} 

find . -type f | while read name ; 
do 
    max2; some_heavy_processing_command ${name} & 
done 
wait 
+2

おい、これはすごくうまくいく!ありがとう! :) – mkgrunder

+0

これはwhile構文を変更した後、私のために働いた:while [$(jobs | wc -l)-ge 2] –

4

は私が私だと思いますを使用してより便利な解決法を見つけました:

#!/usr/bin/make -f 

THIS := $(lastword $(MAKEFILE_LIST)) 
TARGETS := $(shell find . -name '*.sh' -type f) 

.PHONY: all $(TARGETS) 

all: $(TARGETS) 

$(TARGETS): 
     some_heavy_processing_command [email protected] 

$(THIS): ; # Avoid to try to remake this makefile 

とします。 'test.mak'を実行し、実行権を追加します。 ./test.makに電話すると、some_heavy_processing_commandが1つずつ呼び出されます。しかし、./test.mak -j 4と呼ぶことができます。そして、4つのサブプロセスを一度に実行します。また、より洗練された方法で使用することができます:./test.mak -j 5 -l 1.5として実行すると、システム負荷が1.5未満で最大5つのサブプロセスが実行されますが、システム負荷が1.5を超える場合はプロセス数が制限されます。

これはよりも柔軟性があり、parallelのように標準ディストリビューションの一部です。

-1

別のオプション:

PARALLEL_MAX=... 
function start_job() { 
    while [ $(ps --no-headers -o pid --ppid=$$ | wc -l) -gt $PARALLEL_MAX ]; do 
    sleep .1 # Wait for background tasks to complete.       
    done 
    "[email protected]" & 
} 
start_job some_big_command1 
start_job some_big_command2 
start_job some_big_command3 
start_job some_big_command4 
... 
-1

ここでは、私はbashやkshからジョブの最大#を制御するために使用される非常に良い機能です。注:pgrepの - 1はwc -lサブプロセスを減算します。

function jobmax 
{ 
    typeset -i MAXJOBS=$1 
    sleep .1 
    while ((($(pgrep -P $$ | wc -l) - 1) >= $MAXJOBS)) 
    do 
     sleep .1 
    done 
} 

nproc=5 
for i in {1..100} 
do 
    sleep 1 & 
    jobmax $nproc 
done 
wait # Wait for the rest 
関連する問題