
バックグラウンドで別のプロセスを実行して実行中の最大プロセス数(この場合は300)を制御するスクリプトがあります。
最初はスクリプトを大まかに実行します。 1〜2msですが、数時間実行した後は、最終的に200ms〜350msの実行時間で線形勾配で速度が遅くなります。私はPID#を維持するために配列を使用していますが、テーブルサイズを減らすためにキーを設定解除していますが、これが犯人であると感じます。
#!/bin/bash
threads=()
threadcounter=0
crd=0;
while true;
do
threadcounter=${#threads[@]}
crdcounter=${#crds[@]}
if [ "$threadcounter" -lt 300 ]
then
s1=$(($(date +%s%N)/1000000))
pidf=$(/opt/remi/php/root/usr/bin/php cli.php initformula $crd >> /tmp/logger) &
pidfid=$!
s2=$(($(date +%s%N)/1000000))
echo "Init " $crd $(expr $s2 - $s1 ) "ms"
threads[$pidfid]+=$pidfid
else
for pid in "${!threads[@]}"; do
if [ ! -d "/proc/${pid}" ]
then
unset threads[$pid]
fi
done;
fi;
if [ "$crd" -gt 9999999 ]
then
echo "completed all";
exit;
fi;
crd=$(expr $crd + 1)
done;
答え1
元のコード。
起動時にcli.php
開始するのにかかる時間を測定しようとしているので、300個のコピーを開始するだけです。これは約1200のプロセスでなければなりません。
次に変数をcrd
300から9999999まで繰り返します。
シェルが
threads
アレイに空きスロットがあると判断した場合は、cli.php
4つのプロセスを使用して新しいスロットを起動します。それ以外の場合は、約300のプロセスを繰り返して、
カーネルが/proc
仮想ファイルシステムを埋め、
ディレクトリが存在するかどうかをテストします。見つからないディレクトリがある場合、そのエントリはアレイ
から削除されます。threads
名前が使用されていない配列がありますcrds
。
最初の300個以降のcli.php
プロセステーブルに利用可能なスロットがある場合、crd変数の各ループは1つの新しいコピーを作成しますが、テーブルがいっぱいになると最大300個のコピーを削除できるため、実行が完了すると300個から約9,967,000個のcli.php
プロセスが開始されますとして知られており、プロセス数はコンピュータの速度、cli.php
実行時間、コンピュータの負荷によって異なります。最適化できるサイズは6個です!
経験的に、最新のシステムでは、1つのプロセスを実行するのに1つのコアで1ミリ秒かかるため、初期実行速度に問題はありません。新しいプロセスを開始するために使用可能なコアが不足すると、起動速度が大幅に変わることが予想されます。
改善する
スピードを上げる1つの方法は! kill -0 $pid
何も殺しません[ ! -d "/proc/${pid}" ]
がkill -0
、プロセスが存在しない場合はエラーを返すことを使用することです。kill
はシェル組み込み(現状のまま[
)ですが、カーネルで行う作業が少なくなります。これは、ほとんどの時間アレイに使用可能なスロットがない場合に最も効率的ですthreads
。
expr
2番目の改善点は、外部プログラム呼び出しの代わりに組み込み操作を使用して$(( ... ))
開始時間を短縮することですcli.php
。これは、ほとんどの時間、アレイに空きスロットがある場合に最も効率的ですlabels
。
より多くの分析を実行するには、cli.php
実行にかかるおおよその時間と実行回数を知る必要があります。
BUGS
Bashのマニュアルセクションで述べたように、BashIt's too big and too slow.
の配列実装には確かに改善の余地があります。
代替実装
作る
フィードバックにxargs
または使用を提供しなさいparallel
。私はしばしば使用することを好みますmake
。まず、cli.php
必要なコピー数を決定します。だから簡単なMakefile
例
%:
\t/opt/remi/php/root/usr/bin/php cli.php initformula $@
ここで \t はタブ文字です。 (この単純なバージョンでは、0から9999999の範囲の数値名を持つファイルがないとします。)次に、makeを次のように呼び出します。
make -O -j 300 $(seq 0 9999999) > /tmp/logger
10,000,000 の cli.php 呼び出し全体が必要な場合。私はcli.phpがエラーを返すときに処理を中止するためにあまりにも多くの手順を実行する必要がない理由をmake
含めることを好みます。xargs
パラメータ
xargs
解決策を見つけるには
seq 0 9999999 | xargs -n 1 -P 300 /opt/remi/php/root/usr/bin/php cli.php initformula > /tmp/logger
これはもっと簡単です。
吹く
しかし、wait -nf
PIDトレースをまったく気にせずに使用するBashソリューションは、OPの好みに適している可能性があります。最初の 300 のプロセスを開始し、いずれかのプロセスが完了したことを検出すると、別のプロセスを開始します。 10,000,000番目のジョブが開始されたら、すべてのジョブが完了するのを待ちます。まったく同じアルゴリズムではありませんが、非常に似ています。
#!/bin/bash
for(crd=0;crd<300;crd++); do
/opt/remi/php/root/usr/bin/php cli.php initformula $crd &
done > /tmp/logger
for(;crd<=9999999;crd++); do
wait -fn
/opt/remi/php/root/usr/bin/php cli.php initformula $crd &
done >> /tmp/logger
wait