Wie messe ich die durchschnittliche Ausführungszeit eines Skripts?

23

Ich habe zwei Skripte, die jeweils die Fakultät einer Zahl berechnen. Ich würde gerne wissen, was schneller ist. Der timeBefehl gibt mir Millisekunden und das Ergebnis ist von Zeit zu Zeit unterschiedlich:

piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.052s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.091s
user    0m0.048s
sys 0m0.036s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.040s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.087s
user    0m0.064s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.068s
sys 0m0.016s
piousbox@piousbox-laptop:~/projects/trash$ 

Wie nehme ich die durchschnittliche Zeit in Anspruch, um das Skript auszuführen? Ich könnte die Ausgabe einer 100er analysieren und mitteln, timeaber ich kann mir vorstellen, dass es eine bessere Lösung gibt.

Victor Piousbox
quelle

Antworten:

4

Nein, Ihre Vorstellung von Durchschnittsbildung ist richtig.

Die Skriptausführung hängt von vielen Faktoren ab. Sie muss jedoch zwischen der Setup-Zeit (Laden des Interpreters in den Speicher, Einrichten und möglicherweise Kompilieren von Code in Bytecode oder Maschinencode) und der tatsächlichen Ausführungszeit aufgeteilt werden.

Um sich besser auf die innere Ausführungszeit zu konzentrieren, führen Sie die Schleife im Skript selbst aus (dh anstatt eine Fakultät zu berechnen, berechnen Sie sie 100 Mal innerhalb einer Ausführung des Skripts. Das Skript wird einmal eingerichtet und die innere Routine wird 100 Mal ausgeführt mal).

Um sich auf die Gesamtzeit zu konzentrieren, führen Sie das Skript hundertmal aus und mitteln die Ergebnisse. Im Idealfall sollten Sie diese Ausführungen so weit trennen, dass das System jedes Mal einen "Referenzstatus" (oder einen Status ohne Skriptbezug) zurückgibt. Beispielsweise wird der Interpreter selbst im Speicher zwischengespeichert, sodass die erste Ausführung des Skripts erheblich langsamer ist als die nachfolgenden.

Um einen besseren Einblick in den Algorithmus zu erhalten, ist meiner Meinung nach der beste Weg (auf einem ansonsten inaktiven Rechner):

  • Wickeln Sie den Algorithmus in eine einzige Funktion.
  • in der Controlling-Anwendung:
    • Rufen Sie die Funktion einmal auf
    • Holen Sie sich die Systemzeit ("Wanduhr") und addieren Sie 10 (oder ein vernünftiges N) Sekunden
    • Geben Sie die Schleife ein und beginnen Sie, Iterationen zu zählen
    • Erhöhen Sie nach jedem Aufruf der Funktion den Zähler
    • Wenn die Systemzeit unter der gespeicherten Zeit liegt, führen Sie eine weitere Schleife durch
    • Ermittelt genau N, möglicherweise Gleitkomma, aus der aktuellen Uhrzeit der Wanduhr
    • Anzeige des durch N geteilten Zählers: Das ist die Anzahl der Iterationen / Sekunde.

Die Anwendung wird nur einmal ausgeführt. Das Setup und das Priming werden bei der ersten nicht zeitgesteuerten Iteration durchgeführt. Dies sollte den Overhead minimieren (mit Ausnahme von möglicherweise zeitgesteuerten Aufrufen).

Wenn die Funktion eine Eingabe empfängt, sollten Sie eine zufällige Folge von Eingaben mit einem PRNG-Seed mit einem festen Wert bereitstellen, um sicherzustellen, dass beide Versionen der getesteten Funktion dieselben Werte erhalten. Dies vermeidet, dass eine Funktion aufgrund von "Glückszahlen" anscheinend besser abschneidet (z. B. erinnere ich mich an eine Variation des Hillsort-Algorithmus, die messbar besser abschneidet, wenn die Anzahl der zu sortierenden Elemente in der Form 2 k –1 mit kleinen k s liegt).

LSerni
quelle
Richtig, danke. Mir ist aufgefallen, dass die nachfolgenden Anrufe immer kürzer wurden. Ich habe die Schleife jetzt in den Skripten ausgeführt und festgestellt, dass ein Algorithmus definitiv schneller ist als der andere.
Victor Piousbox
39

Sie können Iterationen des Programms in einer Schleife ausführen. und dividieren Sie die Gesamtzeit durch die Anzahl der Iterationen:

time for i in {1..10}; do sleep 1; done
real    0m10.052s
user    0m0.005s
sys 0m0.018s
Kent
quelle
2
Super einfach, liebe es. Ich habe es auch noch nie gesehen {1..10}und bin verblüfft, dass es funktioniert. Ich kann es nicht im Bash-Handbuch finden. Das einzig Traurige ist, dass Sie die Verteilung Ihrer Ergebnisse nicht kennen (minimale und maximale Zeit).
12.
@ w00t:man -P 'less +/Brace\ Expansion' bash
user2683246
Vielen Dank @ user2683246! Ich fand es dann auch unter gnu.org/software/bash/manual/bash.html#Brace-Expansion - nette Verwendung von weniger BTW. Jetzt bin ich auch gespannt , wenn diese in bash erscheint ...
w00t
1
Aha, Version 3, 10 Jahre nachdem ich mit bash angefangen habe
w00t
2
Wenn dies für ankommende Googler nicht funktioniert, liegt es möglicherweise daran, dass Sie nicht ausgeführt werden bash. Versuche /bin/bashvorher zu rennen .
Cory Klein
14

Es gibt ein Tool namens multitime , das genau das tut: einen Befehl mehrmals ausführen, messen, wie lange es dauert (real / user / system mit Mittelwert, min / max und Medianzeit, die automatisch berechnet werden)

Zum Beispiel, um ein ähnliches Skript 100 Mal zu messen:

multitime -q -n 100 "fact1.sh"
===> multitime results
1: -q fact1.sh
            Mean        Std.Dev.    Min         Median      Max
real        0.122       0.032       0.086       0.116       0.171       
user        0.148       0.044       0.096       0.137       0.223       
sys         0.023       0.019       0.000       0.014       0.061 
Cyril Chaboisseau
quelle
12

Dies ist alt, aber es tauchte bei Google so hoch auf, als ich nach einem Befehl suchte, den ich zuvor verwendet hatte, aber nicht finden konnte. Wie auch immer, meine bevorzugte Art dies zu tun ist:

perf stat -r 10 -B sleep 1

Dies enthält eine ganze Reihe von Details, einschließlich der durchschnittlichen Ausführungszeit direkt am Ende:

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
quelle
1

Hyperfein ist eine weitere Option.

Beispielnutzung:

hyperfine --warmup 3 'ruby fac1.rb'
Luís Bianchin
quelle
1
Hyperfein ist so viel besser als die anderen Alternativen, dass es lächerlich ist. Erkennung der Läufe benötigt, Aufwärmen, schöne Ausgabe, Markdown-Berichte, in Rost usw. geschrieben
Klas Mellbourn