Ich habe ein einfaches Skript ausgeführt, um eine große (10000000 Zeilen) CSV-Datei mit 6 Feldern zu generieren, in der sich einige Felder in jeder Zeile/Zeile geändert haben, wobei ein while verwendet wurde Schleife. Die Maschine hatte alle (32) CPUs frei, viel RAM (~31 GB) war auch frei.
Ich habe das Skript mit dem Befehl
getaktet/usr/bin/time -v bash script.01.sh
Nachdem ich ungefähr 2 Stunden gelaufen war, bekam ich die folgenden Statistiken:
Zeitgesteuerter Befehl:„bash script.01.sh“
Benutzerzeit (Sekunden):1195,14
Systemzeit (Sekunden):819,71
Prozentsatz der CPU, die dieser Job erhielt:27 %
Verstrichene Zeit (Wanduhr) (h:mm:ss oder m:ss):2:01:10
Durchschnittliche freigegebene Textgröße (KB):0
Durchschnittliche nicht freigegebene Datengröße (KB ):0
Durchschnittliche Stapelgröße (kByte):0
Durchschnittliche Gesamtgröße (kByte):0
Maximale residente Satzgröße (kByte):4976
Durchschnittliche residente Satzgröße (kByte ):0
Größere (I/O-erfordernde) Seitenfehler:0
Kleine Seitenfehler (Rückgewinnung eines Frames):3131983488
Freiwillige Kontextwechsel:22593141
Unfreiwillige Kontextwechsel:10923348
Swaps:0
Dateisystemeingaben:0
Dateisystemausgaben:2182920
Gesendete Socket-Nachrichten:0
Empfangene Socket-Nachrichten:0
Gelieferte Signale:0
Seitengröße (Bytes):4096
Exit-Status:0
Ich möchte wissen, warum mein Skript nur 27 % der CPU verbraucht hat. Das Disk IO war überhaupt nicht viel (siehe vmstat-Ausgabe). Was hat also die Einschränkung verursacht? Der Code im Skript?
Hier ist das Skript:
#!/usr/bin/env bash
number=1
while [[ $number -lt 10000001 ]] ; do
fname="FirstName LastName $"
lname=""
email="[email protected]"
password="1234567890"
altemail="[email protected]"
mobile="9876543210"
echo "$fname,$lname,$email,$password,$altemail,$mobile" >> /opt/list.csv
number=$(expr $number + 1)
done
Akzeptierte Antwort:
Durch die Verwendung von strace
, ich habe gesehen, dass die Zeile
number=$(expr $number + 1)
verursacht eine Verzweigung, Pfadsuche und Ausführung von expr
. (Ich verwende bash 4.2.45 auf Ubuntu). Dieser Dateisystem-, Festplatten- und Prozess-Overhead führte dazu, dass Bash nur etwa 28 % der CPU beanspruchte.
Als ich diese Zeile geändert habe, um nur Shell-integrierte Operationen zu verwenden
((number = number + 1))
bash verbrauchte etwa 98 % der CPU und das Skript lief in einer halben Stunde. Dies war auf einem 1,5-GHz-Celeron mit einer CPU.
Das Skript, wie es ist, macht nichts, was parallel läuft, also werden 32 freie CPUs nicht viel helfen. Sie können es jedoch sicherlich parallelisieren, indem Sie es beispielsweise in 10 1-Million-Iterationsschleifen aufteilen, die parallel ausgeführt werden, in 10 verschiedene Dateien schreiben und dann cat
verwenden um sie zu kombinieren.
Das folgende Beispielprogramm wurde von @Arthur2e5 hinzugefügt:
max=1000000 step=40000 tmp="$(mktemp -d)"
# Spawning. For loops make a bit more sense in a init-test-incr pattern.
for ((l = 0; l < max; l += step)); do (
for ((n = l + 1, end = (step + l > max ? max : step + l);
n <= end; n++)); do
# Putting all those things into the `buf` line gives you a 1.8x speedup.
fname="FirstName LastName \$"
lname=""
email="[email protected]"
password="1234567890"
altemail="[email protected]"
mobile="9876543210"
buf+="$fname,$lname,$email,$password,$altemail,$mobile"$'\n'
done
printf '%s\n' "$buf" > "$tmp/$l" ) &
done # spawning..
wait
# Merging. The filename order from globbing will be a mess,
# since we didn't format $l to some 0-prefixed numbers.
# Let's just do the loop again.
for ((l = 0; l < max; l += step)); do
printf '%s\n' "$(<"$tmp/$l")" >> /opt/list.csv
done # merging..
rm -rf -- "$tmp" # cleanup