コマンドの同時数の上限と並行してコマンドを実行します


23

順次:for i in {1..1000}; do do_something $i; done-遅すぎます

パラレル:for i in {1..1000}; do do_something $i& done-あまりにも多くの負荷

コマンドを並列に実行するにはどうすればよいのでしょうか?

今では通常for i in {1..1000}; do do_something $i& sleep 5; done、のようなハックを使用していますが、これは良い解決策ではありません。

更新2:受け入れられた回答をスクリプトに変換しました:http : //vi-server.org/vi/parallel

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

動作させるには、「i =」の前に8つのスペースを2つのタブに置き換える必要があることに注意してください。

回答:


15

GNU Parallelはこのために作られました。

seq 1 1000 | parallel -j20 do_something

リモートコンピューターでジョブを実行することもできます。server2とCPUコアごとに1つのジョブを実行するローカルコンピューターを使用してMP3をOGGに再エンコードする例を次に示します。

parallel --trc {.}.ogg -j+0 -S server2,: \
     'mpg321 -w - {} | oggenc -q0 - -o {.}.ogg' ::: *.mp3

GNU Parallelの紹介ビデオをここでご覧ください:

http://www.youtube.com/watch?v=OpaiGYxkSuQ


「moreutils」について知らず、その仕事のためのツールが既にあることを知らない。探して比較します。
Vi。

1
parallelmoreutilsでは、GNUパラレルではなく、非常にそのオプションに限定されています。上記のコマンドは、moreutilsからのパラレルでは実行されません。
オレ丹下

1
もう1つのオプション:xargs --max-procs=20
Vi。

4

bashソリューションではありませんが、Makefileを使用する必要があります-l。最大負荷を超えないようにしてください。

NJOBS=1000

.PHONY = jobs
jobs = $(shell echo {1..$(NJOBS)})

all: $(jobs)

$(jobs):
    do_something $@

その後、一度に20個のジョブを開始するには

$ make -j20

または、5の負荷を超えずにできるだけ多くのジョブを開始する

$ make -j -l5

今の非ハックソリューションのように見えます。
Vi。

2
echo -e 'PHONY=jobs\njobs=$(shell echo {1..100000})\n\nall: ${jobs}\n\n${jobs}:\n\t\techo $@; sleep `echo $$RANDOM/6553 | bc -l`' | make -f - -j20今では再び、よりハックに見えます。
Vi。

@vi:私のああ....
ベンジャミンBannier

スクリプトにあなたのソリューションを変換します。今では簡単に使用することができます。
Vi。

2

書式設定で問題になっているスクリプトを投稿:

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

「i =」の前に8つのスペースを2つのタブに置き換える必要があることに注意してください。


1

一つのシンプルなアイデア:

20を法とするiをチェックし、do_somethingの前にwaitシェルコマンドを実行します。


現在のすべてのタスクが完了するまで待機する(タスク数プロットでサグを作成する)か、長時間停止する可能性がある特定のタスクを1つ待機する(この場合もサグを作成する)
Vi。

@Vi:シェルウェイトがこのシェルに所属するすべてのバックグラウンドタスクのためです。
-harrymc

1

ps実行中のプロセスの数をカウントするために使用でき、これが特定のしきい値を下回るたびに別のプロセスを開始します。

擬似コード:

i = 1
MAX_PROCESSES=20
NUM_TASKS=1000
do
  get num_processes using ps
  if num_processes < MAX_PROCESSES
    start process $i
    $i = $i + 1
  endif
  sleep 1 # add this to prevent thrashing with ps
until $i > NUM_TASKS

1
for i in {1..1000}; do 
     (echo $i ; sleep `expr $RANDOM % 5` ) &
     while [ `jobs | wc -l` -ge 20 ] ; do 
         sleep 1 
     done
done

かもしれないwhile [ `jobs | wc -l` -ge 20]; do
Vi。

確かに、しかし、私のサンプルではnjobs2回計算する必要があり、スリープタスクを実行するシェルスクリプトではパフォーマンスが非常に重要です;)
msw

あなたのバージョンが期待通りに動作しないということです。に変更sleep 1するsleep 0.1と、njobの平均は20ではなく40〜50になります。20を超えるジョブがある場合は、1秒だけ待つのではなく、ジョブが完了するまで待つ必要があります。
Vi。

0

このようにすることができます。

threads=20
tempfifo=$PMS_HOME/$$.fifo

trap "exec 1000>&-;exec 1000<&-;exit 0" 2
mkfifo $tempfifo
exec 1000<>$tempfifo
rm -rf $tempfifo

for ((i=1; i<=$threads; i++))
do
    echo >&1000
done

for ((j=1; j<=1000; j++))
do
    read -u1000
    {
        echo $j
        echo >&1000
    } &
done

wait
echo "done!!!!!!!!!!"

名前付きパイプを使用して、毎回、20個のサブシェルを並行して実行します。

それが役立つことを願っています:)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.