Ich habe mir ein Skript ausgedacht, das einfach einen anderen Prozess im Hintergrund ausführt, der versucht, die maximale Anzahl laufender Prozesse (in diesem Fall 300) zu kontrollieren.
Es führt Skripte zunächst mit ca. 1-2 Millisekunden aus, verlangsamt sich aber nach mehreren Stunden Betrieb schließlich linear auf 200 ms - 350 ms Ausführungszeit. Ich verwende ein Array, um die PID-Nummer beizubehalten, lösche aber auch den Schlüssel, um die Tabellengröße zu verringern, aber ich habe das Gefühl, dass das der Übeltäter ist.
#!/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;
Antwort1
Der Originalcode.
Zu Beginn starten Sie einfach 300 Kopien von cli.php
. Dies erfordert etwa 1200 Prozesse, da Sie die zum Starten benötigte Zeit messen möchten.
Anschließend durchlaufen Sie die Variable in einer Schleife crd
von 300 bis 9999999.
Wenn die Shell der Meinung ist, dass im
threads
Array freie Steckplätze vorhanden sind, startet siecli.php
mithilfe der vier Prozesse einen neuen.Andernfalls durchlaufen Sie ungefähr 300 Prozesse, um den
Kernel dazu zu bringen, das/proc
virtuelle Dateisystem zu füllen und zu prüfen, ob ein
Verzeichnis vorhanden ist. Fehlende Verzeichnisse führen dazu, dass Einträge
aus demthreads
Array gelöscht werden.
Sie haben ein ungenutztes Array namens crds
.
Denn nach den ersten 300 erstellt jede Schleife der CRD-Variable eine neue Kopie, cli.php
wenn in der Prozesstabelle freie Slots vorhanden sind, kann aber bis zu 300 entfernen, wenn die Tabelle voll ist. Am Ende des Laufs wussten wir nur, dass zwischen 300 und ungefähr 9.967.000 cli.php
Prozesse gestartet wurden, wobei die Zahl von der Geschwindigkeit Ihrer Maschine, der Ausführungsdauer cli.php
und der Maschinenlast abhängt. 6 Größenordnungen sind eine Menge, die optimiert werden muss!
Als Faustregel gilt, dass auf einer modernen Maschine das Starten eines Prozesses auf einem Kern 1 ms dauert. Ihre anfängliche Startrate ist also nicht schlecht. Ich würde einen deutlichen Rückgang der Startrate erwarten, wenn Sie keine freien Kerne mehr haben, um neue Prozesse zu starten.
Verbesserungen
Eine Möglichkeit, dies zu beschleunigen, wäre, anstelle ! kill -0 $pid
von zu verwenden . Dies beendet nichts, gibt aber eine Fehlermeldung zurück, wenn der Prozess nicht existiert. ist eine Shell-integrierte Funktion (wie ), aber der Arbeitsaufwand für den Kernel ist geringer. Dies ist am effektivsten, wenn die meiste Zeit keine freien Slots im Array vorhanden sind.[ ! -d "/proc/${pid}" ]
kill -0
kill
[
threads
Eine zweite Verbesserung wäre, den Aufruf des externen Programms expr
durch die Verwendung der integrierten $(( ... ))
Arithmetik zu ersetzen und so den Aufwand für das Starten einer Kopie von zu reduzieren cli.php
. Dies ist am effektivsten, wenn im labels
Array die meiste Zeit freie Slots vorhanden sind.
Um weitere Analysen durchführen zu können, müssen wir die ungefähre Laufzeit cli.php
und die Anzahl der Durchläufe kennen.
Wie im BUGS
Abschnitt im Bash-Handbuch beschrieben It's too big and too slow.
, besteht durchaus Raum für Verbesserungen bei der Array-Implementierung in Bash.
Alternative Implementierungen
machen
In den Kommentaren wird empfohlen, xargs
oder zu verwenden parallel
. Ich bevorzuge häufig die Verwendung von . make
Als erstes muss bestimmt werden, wie viele Kopien von cli.php
gewünscht werden. Dann ein einfaches Makefile
wie
%:
\t/opt/remi/php/root/usr/bin/php cli.php initformula $@
wobei \t ein Tabulatorzeichen ist. (Diese einfache Version geht davon aus, dass Sie keine Dateien mit numerischen Namen im Bereich von 0 bis 9999999 haben.) Rufen Sie dann make as auf.
make -O -j 300 $(seq 0 9999999) > /tmp/logger
wenn Sie die vollen 10.000.000 cli.php-Aufrufe möchten. Gründe, warum ich es vorziehe, make
einzuschließen xargs
, dass keine übermäßigen Schritte erforderlich sind, um die Verarbeitung abzubrechen, wenn cli.php Fehler zurückgibt.
xargs
Für eine xargs
Lösung versuchen Sie
seq 0 9999999 | xargs -n 1 -P 300 /opt/remi/php/root/usr/bin/php cli.php initformula > /tmp/logger
was einfacher ist.
Schlag
Eine Bash-Lösung, die wait -nf
die PIDs verwendet und sich nicht darum kümmert, sie zu verfolgen, könnte jedoch eher dem Geschmack des OP entsprechen. Sie startet die ersten 300 Prozesse und startet dann, wenn sie erkennt, dass einer von ihnen beendet ist, einen weiteren. Sobald der 10.000.000. gestartet wurde, wartet sie ein letztes Mal, bis alle Jobs beendet sind. Nicht genau der gleiche Algorithmus, aber sehr nah dran.
#!/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