Was ist der schnellste Weg, um ein Skript auszuführen?

22

Ich habe mich gefragt, wie man ein Skript am schnellsten ausführt. Ich habe gelesen, dass es einen Unterschied in der Geschwindigkeit gibt, wenn man die Ausgabe des Skripts auf dem Terminal anzeigt, in eine Datei umleitet oder vielleicht /dev/null.

Also, wenn die Ausgabe nicht wichtig ist, was ist der schnellste Weg, um das Skript schneller zum Laufen zu bringen, auch wenn es minimiert ist.

bash ./myscript.sh 
-or-
bash ./myscript.sh > myfile.log
-or-
bash ./myscript.sh > /dev/null
Kingofkech
quelle
Der Vergleich von "Weiterleiten an normale Datei" und "Weiterleiten an / dev / null" erscheint mir so seltsam ...
el.pescado

Antworten:

31

Terminals sind heutzutage langsamer als früher, vor allem, weil Grafikkarten sich nicht mehr um die 2D-Beschleunigung kümmern. In der Tat kann das Drucken auf einem Terminal ein Skript verlangsamen, insbesondere wenn es sich um einen Bildlauf handelt.

Folglich ./script.shist langsamer als ./script.sh >script.log, was wiederum langsamer als ist /script.sh >/dev/null, weil letztere weniger Arbeit erfordern. Ob dies jedoch für einen praktischen Zweck ausreicht, hängt davon ab, wie viel Ausgabe Ihr Skript erzeugt und wie schnell. Wenn Ihr Skript 3 Zeilen schreibt und beendet oder alle paar Stunden 3 Seiten druckt, müssen Sie sich wahrscheinlich nicht um Umleitungen kümmern.

Edit: Einige schnelle (und völlig kaputte) Benchmarks:

  • In einer Linux-Konsole, 240x75:

    $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done)
    real    3m52.053s
    user    0m0.617s
    sys     3m51.442s
  • In einer xtermAuflösung von 260 x 78:

    $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done)
    real    0m1.367s
    user    0m0.507s
    sys     0m0.104s
  • Weiterleiten an eine Datei auf einer Samsung SSD 850 PRO 512 GB-Festplatte:

     $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done >file)
     real    0m0.532s
     user    0m0.464s
     sys     0m0.068s
  • Weiterleiten an /dev/null:

     $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done >/dev/null)
     real    0m0.448s
     user    0m0.432s
     sys     0m0.016s
Satō Katsura
quelle
6
@Kingofkech das sind weniger als 200 Zeilen / Sekunde. Es würde keinen großen Unterschied machen. (Zum Vergleich: timeout 1 yes "This is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line"druckt in einer Sekunde mehr als 100000 Zeilen auf meinem MBP.)
muru
4
@Kingofkech Wenn dies ein Skript ist, bearbeiten Sie eine Datei und kommentieren Sie die Zeile aus, in der unnötige Ausgaben ausgegeben werden. Sie werden viel gewinnen, vor allem, wenn dies ein externer Befehl ist (nicht die eingebaute Shell), der 3 Millionen Mal ausgeführt wird ...
jimmij
2
Für eine enge Auslegung von "früher". Ein VT220-Terminal ist viel langsamer als die heutigen Terminalemulatoren. Außerdem hatten SUN Sparc-Workstations (die von mir verwendeten) eine sehr langsame Konsole. Wenn Sie die Ausgabe beim Kompilieren eines größeren Projekts in eine Datei umleiten, wird die Kompilierzeit erheblich verkürzt.
Kusalananda
1
@Kusalananda Das stimmt, xterms auf HP Apollo vor 30 Jahren verwendet, um im Vergleich zu xtermsauf HP-UX vor 20 Jahren zu kriechen . Eine Linux-Konsole mit einer Matrox-Grafikkarte von vor 15 Jahren war jedoch langsamer als dieselbe Linux-Konsole mit einer S3-Karte von vor 20 Jahren. Und eine Linux-Konsole mit hochauflösendem Bildspeicher auf einer modernen Karte ist einfach unbrauchbar. :)
Satō Katsura
6
@Kingofkech Das sind ungefähr 2400 bps. Einige von uns lebten tatsächlich jahrelang mit diesen Geschwindigkeiten. :)
Satō Katsura
14

Ich hätte der Antwort von Satō Katsura instinktiv zugestimmt. es macht Sinn. Es ist jedoch leicht genug zu testen.

Ich habe getestet, wie man eine Million Zeilen auf den Bildschirm schreibt, an eine Datei anfügt und zu dieser umleitet /dev/null. Ich habe diese nacheinander getestet und dann fünf Wiederholungen durchgeführt. Dies sind die Befehle, die ich verwendet habe.

$ time (for i in {1..1000000}; do echo foo; done)
$ time (for i in {1..1000000}; do echo foo; done > /tmp/file.log) 
$ time (for i in {1..1000000}; do echo foo; done > /dev/null)

Ich habe dann die Gesamtzeiten unten eingetragen.

Diagramm von Zeit gegen Ausgabe

Wie Sie sehen, stimmten die Vermutungen von Satō Katsura. Gemäß der Antwort von Satō Katsura bezweifle ich auch, dass der limitierende Faktor die Ausgabe ist, so dass es unwahrscheinlich ist, dass die Wahl der Ausgabe einen wesentlichen Einfluss auf die Gesamtgeschwindigkeit des Skripts hat.

FWIW, meine ursprüngliche Antwort hatte einen anderen Code, bei dem die Datei angehängt und in der Schleife /dev/nullumgeleitet wurde .

$ rm /tmp/file.log; touch /tmp/file.log; time (for i in {1..1000000}; do echo foo >> /tmp/file.log; done) 
$ time (for i in {1..1000000}; do echo foo > /dev/null; done)

Wie John Kugelman in den Kommentaren ausführt, bedeutet dies einen erheblichen Mehraufwand. Da die Frage steht, ist dies nicht wirklich der richtige Weg , um es zu testen, aber ich werde es hier lassen , wie es deutlich die Kosten für die Wiedereröffnung eine Datei wiederholt aus zeigt im Skript selbst.

Diagramm von Zeit gegen Ausgabe

In diesem Fall werden die Ergebnisse umgekehrt.

Sparhawk
quelle
FWIW Ich habe meiner Antwort einen kurzen Benchmark hinzugefügt. Bemerkenswerterweise ist die Linux-Konsole> 200-mal langsamer als eine xterm, was wiederum ~ 3-mal langsamer ist als /dev/null.
Satō Katsura
Sie sollten auch mit einer Geschwindigkeitsbegrenzung testen. Die Ausgabe des OP beträgt ca. 200 Zeilen / s.
muru
@muru Meinst du eine Zeile drucken, 1/200 Sekunden warten und dann wiederholen? Ich kann es versuchen, aber ich denke, es wird ähnliche Ergebnisse geben, aber es dauert nur viel länger, bis das Signal das Rauschen überwunden hat. Obwohl ich vielleicht die Wartezeit vor der Analyse abziehen kann.
Sparhawk
@Sparhawk sowas. Ich denke, dass die CPU bei dieser Ausgabe viel Zeit hat, um die Anzeige zu aktualisieren, ohne die Ausgaberate zu verlangsamen. Wenn das Programm ohne Pause nur Zeilen spuckt, werden die Terminalpuffer schneller gefüllt, als die Anzeige aktualisiert werden kann, und es entsteht ein Engpass.
muru
3

Eine andere Möglichkeit, ein Skript zu beschleunigen, ist die Verwendung eines schnelleren Shell-Interpreters. Vergleichen Sie die Geschwindigkeiten einer POSIX- Busy-Schleife, die unter bash v4.4 , ksh v93u + 20120801 und dash v0.5.8 ausgeführt wird .

  1. bash:

    time echo 'n=0;while [ $n -lt 1000000 ] ; do \
                      echo $((n*n*n*n*n*n*n)) ; n=$((n+1)); 
                   done' | bash -s > /dev/null

    Ausgabe:

    real    0m25.146s
    user    0m24.814s
    sys 0m0.272s
  2. ksh:

    time echo 'n=0;while [ $n -lt 1000000 ] ; do \
                      echo $((n*n*n*n*n*n*n)) ; n=$((n+1)); 
                   done' | ksh -s > /dev/null

    Ausgabe:

    real    0m11.767s
    user    0m11.615s
    sys 0m0.010s
  3. dash:

    time echo 'n=0;while [ $n -lt 1000000 ] ; do \
                      echo $((n*n*n*n*n*n*n)) ; n=$((n+1)); 
                   done' | dash -s > /dev/null

    Ausgabe:

    real    0m4.886s
    user    0m4.690s
    sys 0m0.184s

Eine Teilmenge von Befehlen in bashund kshist abwärtskompatibel zu allen Befehlen in dash. Ein bashSkript, das nur Befehle in dieser Teilmenge verwendet, sollte mit funktionieren dash.

Einige bashSkripte, die neue Funktionen verwenden, können in einen anderen Interpreter konvertiert werden. Wenn sich das bashSkript stark auf neuere Funktionen stützt, lohnt sich die Mühe möglicherweise nicht. Einige neue bashFunktionen sind Verbesserungen, die einfacher zu programmieren und effizienter sind (obwohl bashsie im Allgemeinen langsamer sind), sodass das dashÄquivalent (das möglicherweise ausgeführt werden muss) mehrere andere Befehle), wäre langsamer.

Führen Sie im Zweifelsfall einen Test durch ...

agc
quelle
Also, um mein Bash-Skript zu beschleunigen, muss ich sie in Bash oder Ksh umschreiben?
Kingofkech
@Kingofkech Es kann sein, dass der Code, den Sie für bash geschrieben haben, ohne weiteres korrekter ksh- oder dash-Code ist. Versuchen Sie einfach, den Interpreter zu ändern.
bli