原始碼。

原始碼。

我有一個腳本,它只是在背景執行另一個進程,試圖控制運行的最大進程數(在本例中為 300)。

它最初大約執行腳本。 1-2 毫秒,但運行幾個小時後,它最終會以線性斜率減慢到 200 毫秒 - 350 毫秒執行時間。我使用陣列來維護 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

原始碼。

當您開始時,您只需啟動 300 份cli.php.這大約需要 1200 個進程,因為您想要測量啟動所需的時間。

然後將變數crd從 300 循環到 9999999。

  • 如果 shell 認為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 毫秒,因此您在初始啟動速率下的表現還不錯。我預計一旦你用完可用的核心來啟動新進程,啟動率就會出現顯著的變化。

改進

加快速度的一種方法是使用! kill -0 $pid-[ ! -d "/proc/${pid}" ]kill -0殺死任何東西,但如果進程不存在則回傳錯誤。kill是一個 shell 內建函數(原樣[),但核心要做的工作量較小。如果大多數時候陣列中沒有空閒槽,這將是最有效的threads

expr第二個改進是使用內建算術取代對外部程式的調用$(( ... )),從而減少啟動cli.php.如果陣列中大部分時間都有空閒槽,則這是最有效的labels

為了進行更多分析,我們需要知道cli.php運行所需的大致時間以及運行次數。

如同BUGSbash 手冊中的部分所述It's too big and too slow.,bash 中的陣列實作肯定還有改進的空間。

替代實現

製作

在評論中建議使用xargsparallel。我常常比較喜歡使用make.首先要確定cli.php需要多少份副本。然後一個簡單Makefile的例如

%:
\t/opt/remi/php/root/usr/bin/php cli.php initformula $@

其中 \t 是製表符。 (這個簡單版本假設您沒有任何數字名稱在 0 到 9999999 範圍內的檔案)。然後呼叫 make as

make -O -j 300 $(seq 0 9999999) > /tmp/logger

如果您想要完整的 10,000,000 次 cli.php 呼叫。我更喜歡make包含xargs在 cli.php 傳回錯誤時不需要採取過多步驟來中止處理的原因。

參數

如需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

相關內容