Lassen Sie mich ein Beispiel geben:
$ timeout 1 yes "GNU" > file1
$ wc -l file1
11504640 file1
$ for ((sec0=`date +%S`;sec<=$(($sec0+5));sec=`date +%S`)); do echo "GNU" >> file2; done
$ wc -l file2
1953 file2
Hier können Sie sehen, dass der Befehl Zeilen in einer Sekunde yes
schreibt 11504640
, während ich 1953
mit bashs for
und nur Zeilen in 5 Sekunden schreiben kann echo
.
Wie in den Kommentaren vorgeschlagen, gibt es verschiedene Tricks, um die Effizienz zu steigern, aber keine entspricht in etwa der Geschwindigkeit von yes
:
$ ( while :; do echo "GNU" >> file3; done) & pid=$! ; sleep 1 ; kill $pid
[1] 3054
$ wc -l file3
19596 file3
$ timeout 1 bash -c 'while true; do echo "GNU" >> file4; done'
$ wc -l file4
18912 file4
Diese können bis zu 20.000 Zeilen pro Sekunde schreiben. Und sie können weiter verbessert werden, um:
$ timeout 1 bash -c 'while true; do echo "GNU"; done >> file5'
$ wc -l file5
34517 file5
$ ( while :; do echo "GNU"; done >> file6 ) & pid=$! ; sleep 1 ; kill $pid
[1] 5690
$ wc -l file6
40961 file6
Diese bringen uns in einer Sekunde bis zu 40.000 Zeilen. Besser, aber noch weit entfernt, mit yes
dem man in einer Sekunde ungefähr 11 Millionen Zeilen schreiben kann!
Also, wie yes
schreibt man so schnell in eine Datei?
date
sind etwas schwergewichtig. Außerdem muss die Shell den Ausgabestreamecho
für jede Schleifeniteration erneut öffnen . Im ersten Beispiel gibt es nur einen einzigen Befehlsaufruf mit einer einzigen Ausgabeumleitung, und der Befehl ist äußerst kompakt. Die beiden sind in keiner Weise vergleichbar.date
kann schwergewichtig sein, siehe meine frage bearbeiten.timeout 1 $(while true; do echo "GNU">>file2; done;)
ist die falsche Methode,timeout
da dertimeout
Befehl erst gestartet wird, wenn die Befehlsersetzung abgeschlossen ist. Verwenden Sietimeout 1 sh -c 'while true; do echo "GNU">>file2; done'
.write(2)
Indem Sie in Ihrem ersten Beispiel nur CPU-Zeit für Systemaufrufe verwenden, nicht für Bootloads anderer Systemaufrufe, Shell-Overhead oder sogar für die Erstellung von Prozessen (die ausgeführt werden und aufdate
jede Zeile warten, die in der Datei gedruckt wird). Eine Sekunde des Schreibens reicht kaum aus, um auf einem modernen System mit viel RAM einen Engpass bei der Datenträger-E / A (anstelle von CPU / Speicher) zu verursachen. Wenn länger laufen darf, wäre der Unterschied geringer. (Abhängig davon, wie schlecht eine Bash-Implementierung ist und wie schnell die CPU und der Datenträger sind, können Sie die Datenträger-E / A mit Bash möglicherweise nicht einmal auslasten.)Antworten:
Nussschale:
yes
zeigt ein ähnliches Verhalten wie die meisten anderen Standarddienstprogramme, die normalerweise in einen FILE STREAM schreiben , wobei die Ausgabe von der libC über stdio gepuffert wird . Diese rufen nur alle 4 kb (16 kb oder 64 kb) oder unabhängig vom Ausgangsblock BUFSIZ das System auf . ist ein pro . Das ist eine Menge von Modus-Umschaltung (das ist anscheinend nicht so teuer , wie ein Kontext-Switch ) .write()
echo
write()
GNU
Und das ist überhaupt nicht zu erwähnen, dass es sich neben der anfänglichen Optimierungsschleife
yes
um eine sehr einfache, winzige, kompilierte C-Schleife handelt und Ihre Shell-Schleife in keiner Weise mit einem vom Compiler optimierten Programm vergleichbar ist.aber ich habe mich getäuscht:
Als ich vorher sagte, dass
yes
stdio verwendet wird, habe ich nur angenommen, dass dies der Fall ist, weil es sich sehr ähnlich verhält wie diejenigen, die dies tun. Dies war nicht korrekt - es emuliert nur ihr Verhalten auf diese Weise. Was es tatsächlich tut, ist sehr ähnlich wie das, was ich unten mit der Shell gemacht habe: Es schleift zuerst, um seine Argumente (odery
wenn keine vorhanden sind) zu verknüpfen, bis sie nicht mehr wachsen, ohne zu überschreitenBUFSIZ
.Ein Kommentar aus der Quelle unmittelbar vor der betreffenden
for
Schleife lautet:yes
macht sein macht sein eigeneswrite()
s danach.Abschweifung:
(Wie ursprünglich in der Frage enthalten und für den Kontext zu einer möglicherweise informativen Erklärung beibehalten, die bereits hier geschrieben wurde) :
Das
timeout
Problem, das Sie mit der Befehlsersetzung haben - ich glaube, ich verstehe es jetzt und kann erklären, warum es nicht aufhört.timeout
startet nicht, weil die Kommandozeile nie ausgeführt wird. Ihre Shell gabelt eine untergeordnete Shell, öffnet eine Pipe auf ihrer Standardausgabe und liest sie. Es hört auf zu lesen, wenn das Kind aufhört, und interpretiert dann alles, was das Kind für$IFS
Mangeln und Glob-Erweiterungen geschrieben hat, und ersetzt mit den Ergebnissen alles von$(
bis zum Matching)
.Aber wenn das Kind eine Endlosschleife, die nie an das Rohr schreibt, dann nie das Kind hält Looping und
timeout
‚s - Befehlszeile nie zuvor abgeschlossen ist (wie ich denke) Sie tunCTRL-C
und das Kind Schleife töten. Sotimeout
kann niemals die Schleife töten , die abgeschlossen werden muss , bevor es gestartet werden kann.andere
timeout
s:... sind für Ihre Performance-Probleme einfach nicht so relevant wie die Zeit, die Ihr Shell-Programm für den Wechsel zwischen Benutzer- und Kernel-Modus benötigt, um die Ausgabe zu erledigen.
timeout
Es ist jedoch nicht so flexibel wie eine Shell für diesen Zweck: Wo Shells Excel in der Lage ist, Argumente zu entstellen und andere Prozesse zu verwalten.Wie an anderer Stelle erwähnt,
[fd-num] >> named_file
kann die Leistung erheblich verbessert werden, wenn Sie Ihre Umleitung einfach auf das Ausgabeziel der Schleife verschieben, anstatt nur die Ausgabe für den übergebenen Befehl dorthin zu leiten, da auf diese Weise zumindest deropen()
Systemaufruf nur einmal ausgeführt werden muss. Dies geschieht auch weiter unten mit dem|
Rohr, das als Ausgang für die inneren Schleifen vorgesehen ist.direkter Vergleich:
Das könnte Ihnen gefallen:
Das ist Art von wie die Befehlsunter Beziehung zuvor beschrieben, aber es gibt keine Pfeife und das Kind backgrounded, bis sie die Eltern töten. In dem
yes
Fall, dass der Elternteil tatsächlich ersetzt wurde, seit das Kind erzeugt wurde, aber die Shell ruft auf,yes
indem sie ihren eigenen Prozess mit dem neuen überlagert, sodass die PID gleich bleibt und das Zombie-Kind immer noch weiß, wen es töten soll.größerer Puffer:
Nun sehen wir uns an, wie der
write()
Puffer der Shell erhöht wird .Ich habe diese Zahl gewählt, weil die Ausgabe-Strings, die länger als 1 KB sind,
write()
für mich in separate aufgeteilt wurden . Und hier ist nochmal die Schleife:Das ist das 300-fache der Datenmenge, die von der Shell in derselben Zeit für diesen Test geschrieben wurde wie für den letzten. Nicht zu schäbig. Ist es aber nicht
yes
.verbunden:
Wie gewünscht, gibt es eine ausführlichere Beschreibung als die bloßen Code-Kommentare zu dem, was hier unter diesem Link gemacht wird .
quelle
dd
ist ein Standardwerkzeug, das zum Beispiel definitiv kein stdio verwendet. die meisten anderen tun es.open
(existierendes)write
ANDclose
(was meiner Meinung nach immer noch auf Flush wartet) UND erstellt einen neuen Prozess unddate
führt ihn für jede Schleife aus.wc -l
Schleife mit mache, bekomme ich ein Fünftelbash
der Ausgabe, die diesh
Schleife macht -bash
verwaltet etwas mehr als 100kwrites()
bisdash
zu 500k.for((sec0=`date +%S`;...
zu steuernden Zeit und der Umleitung in der Schleife hatte, nicht die nachträglichen Verbesserungen.Eine bessere Frage wäre, warum Ihre Shell die Datei so langsam schreibt. Jedes in sich geschlossene kompilierte Programm, das Systemaufrufe zum Schreiben von Dateien verantwortungsbewusst ausführt (nicht jedes Zeichen gleichzeitig löscht), würde dies relativ schnell tun. Was Sie tun, ist das Schreiben von Zeilen in einer interpretierten Sprache (der Shell), und außerdem führen Sie eine Menge unnötiger Eingabe- / Ausgabeoperationen aus. Was
yes
macht:Was Ihr Skript macht:
date
externe Kommando auf und speichern Sie dessen Ausgabe (nur in der Originalversion - in der überarbeiteten Version erhalten Sie den Faktor 10, wenn Sie dies nicht tun)echo
Kommando, erkenne es (mit einigem Pattern Matching Code) als eine eingebaute Shell, rufe die Parametererweiterung und alles andere für das Argument "GNU" auf und schreibe schließlich die Zeile in die geöffnete DateiDie teuren Teile: Die gesamte Interpretation ist extrem teuer (Bash macht eine Menge Vorverarbeitung aller Eingaben - Ihre Zeichenkette könnte möglicherweise variable Substitution, Prozesssubstitution, Klammererweiterung, Escape-Zeichen und mehr enthalten), jeder Aufruf eines eingebauten Befehls ist wahrscheinlich eine switch-Anweisung mit Weiterleitung zu einer Funktion, die sich mit dem eingebauten Code befasst, und vor allem öffnen und schließen Sie eine Datei für jede einzelne Ausgabezeile. Sie können
>> file
die while-Schleife auch außerhalb der Schleife platzieren, um den Vorgang zu beschleunigen , aber Sie sprechen immer noch eine interpretierte Sprache. Sie haben das große Glückecho
ist eine eingebaute Shell, kein externer Befehl - andernfalls müsste in Ihrer Schleife bei jeder einzelnen Iteration ein neuer Prozess (fork & exec) erstellt werden. Was den Prozess zum Stillstand bringen würde - Sie haben gesehen, wie kostspielig das war, als Sie dendate
Befehl in der Schleife hatten.quelle
Die anderen Antworten haben die Hauptpunkte angesprochen. Nebenbei bemerkt, Sie können den Durchsatz Ihrer while-Schleife erhöhen, indem Sie am Ende der Berechnung in die Ausgabedatei schreiben. Vergleichen Sie:
mit
quelle