Universelle Non-Bash-Zeit-Benchmark-Alternative? [geschlossen]

10

Für den Vergleich der Laufzeiten von Skripten zwischen verschiedenen Shells empfehlen einige SE-Antworten die Verwendung bash des integrierten time Befehls wie folgt:

time bash -c 'foo.sh'
time dash -c 'foo.sh'

... usw. , damit jede Shell getestet werden kann. Solche Benchmarks eliminieren nicht die Zeit, die jede Shell benötigt, um sich selbst zu laden und zu initialisieren . Angenommen, beide oben genannten Befehle wurden auf einem langsamen Gerät mit der Lesegeschwindigkeit einer frühen Diskette gespeichert (124 KB / s) dash(eine ausführbare Datei mit ~ 150 KB), die etwa 7x schneller als die Shell bash( ~ 1 MB ) geladen würde Die Ladezeit würde die timeZahlen verzerren - die Vorladezeiten dieser Schalen sind für die Messung der Laufzeiten foo.shunter jeder Schale nach dem Laden der Schalen irrelevant .

Was ist das beste tragbare und allgemeine Dienstprogramm für das Skript-Timing, das in jeder Shell ausgeführt werden kann? Der obige Code würde also ungefähr so ​​aussehen:

bash -c 'general_timer_util foo.sh'
dash -c 'general_timer_util foo.sh'

NB: keine Shell integrierten time Befehle, da keine portabel oder allgemein sind.


Besser noch, wenn der Util auch die Zeit messen kann, die die internen Befehle und Pipelines einer Shell benötigen, ohne dass der Benutzer sie zuerst in ein Skript einbinden muss. Eine künstliche Syntax wie diese würde helfen:

general_timer_util "while read x ; do echo x ; done < foo"

Einige Muscheln timekönnen das schaffen. Zum Beispiel bash -c "time while false ; do : ; done"funktioniert. Um zu sehen, was auf Ihrem System funktioniert (und was nicht), versuchen Sie Folgendes:

tail +2 /etc/shells | 
while read s ; do 
    echo $s ; $s -c "time while false ; do : ; done" ; echo ----
done
agc
quelle
6
Einfach benutzen /usr/bin/time?
Kusalananda
1
Ich verstehe nicht, wie ein Nicht-Integrierter möglicherweise sowohl "die Zeit eliminieren kann, die jede Shell benötigt, um sich selbst zu laden und zu initialisieren" als auch ein eigenständiges Skript ausführt, während sie "portabel und allgemein" ist.
Michael Homer
1
Das ist keine Antwort auf die Frage, sondern eine Aufforderung für Sie, zu klären, was Sie wollen.
Michael Homer
1
Ich habe meinen besten Versuch veröffentlicht, aber ich denke, die Frage ist immer noch nicht genau festgelegt, was genau erreicht werden soll.
Michael Homer
2
Was meinst du mit "tragbar oder allgemein"? Die Shell-Buildins sind genauso portabel (arbeiten auf so vielen Systemen) und allgemeiner (arbeiten unter mehr Umständen, da sie etwas anderes als die Ausführung einer Datei zeitlich festlegen können) wie der externe Befehl. Welches Problem versuchen Sie zu lösen?
Gilles 'SO - hör auf böse zu sein'

Antworten:

10

Sie sollten beachten, dass dies timevon POSIX angegeben wird und AFAICT die einzige Option, die POSIX erwähnt ( -p), von verschiedenen Shells korrekt unterstützt wird:

$ bash -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ dash -c 'time -p echo'

real 0.01
user 0.00
sys 0.00
$ busybox sh -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ ksh -c 'time -p echo'       

real 0.00
user 0.00
sys 0.00
muru
quelle
1
Das Problem ist, dass, um Timings vergleichen zu können, das Ergebnis durch dieselbe Implementierung von zeitlich festgelegt werden muss time. Es ist vergleichbar damit, Sprinter ihre eigene Zeit auf 100 m einzeln messen zu lassen, anstatt dies mit einer Uhr gleichzeitig zu tun. Dies ist offensichtlich ein Trottel, aber immer noch ...
Kusalananda
@Kusalananda Ich dachte, das Problem sei, dass OP-Gedanken timenicht portabel sind. es scheint tragbar zu sein. (Ich stimme jedoch Ihrem Punkt zur Vergleichbarkeit zu)
Muru
@muru, auf meinem System dash -c 'time -p while false ; do : ; done'zurückkehrt „Zeit: kann nicht ausgeführt werden, während: Keine solche Datei oder das Verzeichnis <cr> Befehl mit Nicht-Null - Status 127 beendet“ Fehlern.
Agc
1
@agc POSIX sagt außerdem: "Der Begriff Dienstprogramm wird anstelle von Befehlen verwendet, um die Tatsache hervorzuheben, dass Shell-Verbundbefehle, Pipelines, spezielle integrierte Funktionen usw. nicht direkt verwendet werden können. Das Dienstprogramm enthält jedoch Benutzeranwendungsprogramme und Shell-Skripte, nicht nur die Standarddienstprogramme. " (siehe Abschnitt RATIONALE)
Muru
7

Ich benutze den GNU- date Befehl, der einen hochauflösenden Timer unterstützt:

START=$(date +%s.%N)
# do something #######################

"$@" &> /dev/null

#######################################
END=$(date +%s.%N)
DIFF=$( echo "scale=3; (${END} - ${START})*1000/1" | bc )
echo "${DIFF}"

Und dann nenne ich das Skript so:

/usr/local/bin/timing dig +short unix.stackexchange.com
141.835

Die Ausgabeeinheit ist in Millisekunden angegeben.

Rabin
quelle
1
Angenommen, die Zeit (Epochenzeit) ändert sich dazwischen nicht. Ich kann mir keinen Fall in der Praxis vorstellen, in dem es ein Problem verursachen würde, der aber dennoch erwähnenswert ist.
Phk
1
Sie sollten hinzufügen, dass dies datespeziell GNU erfordert .
Kusalananda
@phk bitte erklären?
Rabin
1
@Rabin Nehmen wir an, Ihr NTP-Client hat Probleme und aktualisiert und ändert Ihre Uhr zwischen "eingestellt" STARTund " ENDeingestellt". Dies würde sich offensichtlich auf Ihr Ergebnis auswirken. Keine Ahnung, wie genau Sie es brauchen und ob es in Ihrem Fall wichtig ist, aber wie gesagt, etwas, das Sie beachten sollten. (Unterhaltsame Geschichte: Ich kenne eine Software, bei der genau dies zu unerwartet negativen Ergebnissen führte - sie wurde zur Durchsatzberechnung verwendet - und die dann einige Dinge
kaputt machte
1
Verlangsamen und beschleunigen einige NTP-Clients nicht die Uhr, anstatt "Sprünge" in der Systemzeit zu machen? Wenn Sie einen solchen NTP-Client haben und gestern Abend einige Timings vorgenommen haben, werden diese wahrscheinlich vom NTP-Client verzerrt, der die Schaltsekunde "vorwegnimmt". (Oder läuft die Systemuhr in diesem Fall einfach auf 61?)
Jörg W Mittag
6

Das time Dienstprogramm ist in der Regel in die Schale eingebaut, wie Sie vielleicht bemerkt haben, die es nutzlos als „neutral“ Timer macht.

Das Dienstprogramm ist jedoch normalerweise auch als externes Dienstprogramm verfügbar /usr/bin/time, das durchaus zur Durchführung der von Ihnen vorgeschlagenen Timing-Experimente verwendet werden kann.

$ bash -c '/usr/bin/time foo.sh'
Kusalananda
quelle
Wie wird dadurch "die Zeit eliminiert, die jede Shell benötigt, um sich selbst zu laden und zu initialisieren"?
Michael Homer
1
Wenn foo.shes ausführbar ist und einen Shebang hat, wird es immer mit derselben Shell ausgeführt, und die Startzeit dieser Shell wird gezählt, sodass OP dies nicht wünscht. Wenn foo.sheine davon fehlt, funktioniert dies überhaupt nicht.
Kevin
@ Kevin Sehr wahr. Ich timehabe anscheinend nur "keine eingebaute Shell " berücksichtigt. Die Shell-Startzeit muss möglicherweise separat gemessen werden.
Kusalananda
1
Ich kenne keine Shell mit einem timeeingebauten Befehl. Viele Shells enthalten bashjedoch ein timeSchlüsselwort, mit dem Pipelines zeitlich festgelegt werden können. Um dieses Schlüsselwort zu deaktivieren, damit der timeBefehl (im Dateisystem) verwendet werden kann, können Sie ihn wie folgt zitieren "time" foo.sh. Siehe auch unix.stackexchange.com/search?q=user%3A22565+time+keyword
Stéphane Chazelas
6

Hier ist eine Lösung, die:

  1. Eliminieren Sie [s] die Zeit, die jede Shell benötigt, um sich selbst zu laden und zu initialisieren

  2. kann von innen ausgeführt werden jeder Schale

  3. Verwendet

    Keine in die Shell integrierten timeBefehle, da keine portabel oder allgemein sind

  4. Funktioniert in allen POSIX-kompatiblen Shells.
  5. Funktioniert auf allen POSIX-kompatiblen und XSI-konformen Systemen mit einem C-Compiler oder wenn Sie eine ausführbare C-Datei im Voraus kompilieren können.
  6. Verwendet für alle Shells dieselbe Timing-Implementierung.

Es gibt zwei Teile: ein kurzes C-Programm, das abgeschlossen wird gettimeofday , das veraltet, aber immer noch portabler als ist clock_gettime, und ein kurzes Shell-Skript, das dieses Programm verwendet, um eine Uhr mit Mikrosekundengenauigkeit zu erhalten, die beide Seiten der Beschaffung eines Skripts liest. Das C-Programm ist die einzige tragbare Methode mit minimalem Overhead, um eine Genauigkeit von weniger als einer Sekunde für einen Zeitstempel zu erzielen.

Hier ist das C-Programm epoch.c:

#include <sys/time.h>
#include <stdio.h>
int main(int argc, char **argv) {
    struct timeval time;
    gettimeofday(&time, NULL);
    printf("%li.%06i", time.tv_sec, time.tv_usec);
}

Und das Shell-Skript timer:

#!/bin/echo Run this in the shell you want to test

START=$(./epoch)
. "$1"
END=$(./epoch)
echo "$END - $START" | bc

Dies ist die Standard- Shell-Befehlssprache undbc und soll als Skript unter jedem POSIX-kompatiblen Shell arbeiten.

Sie können dies verwenden als:

$ bash timer ./test.sh
.002052
$ dash timer ./test.sh
.000895
$ zsh timer ./test.sh
.000662

Es misst nicht die System- oder Benutzerzeit, sondern nur die nicht monotone Wanduhr. Wenn sich die Systemuhr während der Ausführung des Skripts ändert, führt dies zu falschen Ergebnissen. Wenn das System unter Last steht, ist das Ergebnis unzuverlässig. Ich denke nicht, dass etwas Besseres zwischen Muscheln tragbar sein kann.

Ein modifiziertes Timer-Skript könnte evalstattdessen Befehle außerhalb eines Skripts ausführen.

Michael Homer
quelle
Zufälligerweise kurz vor der Lektüre dieses, (und die letzte Zeile über eval), war ich zwicken das Skript in Rabin ‚s Antwort zu enthalten eval "$@", so dass es Shell laufen konnte builtins on the fly.
Agc
4

Mehrfach überarbeitete Lösung mit /proc/uptimeund dc/ bc/ awkin großen Teilen dank der Eingabe von agc :

#!/bin/sh

read -r before _ < /proc/uptime

sleep 2s # do something...

read -r after _ < /proc/uptime

duration=$(dc -e "${after} ${before} - n")
# Alternative using bc:
#   duration=$(echo "${after} - ${before}" | bc)
# Alternative using awk:
#   duration=$(echo "${after} ${before}" | awk '{print $1 - $2}')

echo "It took $duration seconds."

Nimmt offensichtlich an, dass es /proc/uptimeexistiert und eine bestimmte Form hat.

phk
quelle
3
Dies würde es zwischen Shells portierbar machen, aber nicht zwischen Unix-Implementierungen portierbar, da einigen einfach das /procDateisystem fehlt . Ob dies ein Problem ist oder nicht, weiß ich nicht.
Kusalananda
1
Zur Hervorhebung schlägt dieses Q Diskettengeschwindigkeiten oder schlechter vor. In diesem Fall kann der Aufwand für das Laden awkerheblich sein. Vielleicht b=$(cat /proc/uptime)vorher, dann a=$(cat /proc/uptime)nachher, dann $ a und $ b analysieren und subtrahieren.
Agc
@agc Gute Eingabe, danke, ich habe dementsprechend einige alternative Lösungen hinzugefügt.
Phk
1
Ich habe vorher nicht daran gedacht, aber wenn Buildins wie readbesser sind als cat, wäre dies sauberer (und etwas schneller): read before dummyvar < /proc/uptime ;sleep 2s;read after dummyvar < /proc/uptime; duration=$(dc -e "${after} ${before} - n");echo "It took $duration seconds."
Agc