
我有一個腳本,它只是在背景執行另一個進程,試圖控制運行的最大進程數(在本例中為 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
運行所需的大致時間以及運行次數。
如同BUGS
bash 手冊中的部分所述It's too big and too slow.
,bash 中的陣列實作肯定還有改進的空間。
替代實現
製作
在評論中建議使用xargs
或parallel
。我常常比較喜歡使用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