元のコード。

元のコード。

バックグラウンドで別のプロセスを実行して実行中の最大プロセス数(この場合は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のプロセスでなければなりません。

次に変数をcrd300から9999999まで繰り返します。

  • シェルがthreadsアレイに空きスロットがあると判断した場合は、cli.php4つのプロセスを使用して新しいスロットを起動します。

  • それ以外の場合は、約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

expr2番目の改善点は、外部プログラム呼び出しの代わりに組み込み操作を使用して$(( ... ))開始時間を短縮することですcli.php。これは、ほとんどの時間、アレイに空きスロットがある場合に最も効率的ですlabels

より多くの分析を実行するには、cli.php実行にかかるおおよその時間と実行回数を知る必要があります。

BUGSBashのマニュアルセクションで述べたように、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 -nfPIDトレースをまったく気にせずに使用する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

おすすめ記事