Für diesen Anwendungsfall -- das Messen der Ausführungszeit eines Programms -- existiert das UNIX-Kommando
time. Es ermittelt diese drei Werte:
- real: die gesamte, tatsächliche Ausführungszeit (elapsed real time)
- user: die Zeit, in der die CPU tatsächlich den (User-)Code des Programms ausführt (Total number of CPU-seconds that the process spent in user mode)
- sys: jene Zeit, in der die CPU mit Ausführung von Kernel-Code für das Programm beschäftigt ist (Total number of CPU-seconds that the process spent in kernel mode)
Beispiel 1
Um die Zeit zu ermitteln, rufen Sie
time vor ihrem eigentlichen Kommando auf:
$ time find . -user frank
...
real 0m3.681s
user 0m0.248s
sys 0m0.916s
$
Das Beispiel ermittelt alle Dateien, die dem Benutzer
frank gehören.
time rechnet aus, dass
find dafür 3.6s benötigt.
Skripting in der Praxis
Spannender wird die Zeitmessung, wenn mehrere Kommandos zusammenspielen, bspw. in einem Shellskript. Nachfolgend geht es darum, die User ID und den dazugehörigen Namen des UNIX-Nutzers zu ermitteln, der ein Skript gerade aufruft.
Variante 1 greift auf interne Variablen und das Kommando
whoami zurück:
#!/bin/bash
userId=$UID
userName=$(whoami)
echo "the script runs as user $userName with UNIX id $userId"
Variante 2 nutzt stattdessen
grep und
cut:
#!/bin/bash
userId=$UID
userName=$(grep ":$UID:" /etc/passwd | cut -d: -f 1)
echo "the script runs as user $userName with UNIX id $userId"
In
Variante 3 kommen
grep und
awk zum Einsatz:
#!/bin/bash
userId=$UID
userName=$(grep ":$UID:" /etc/passwd | awk -F : '{ print $1 }')
echo "the script runs as user $userName with UNIX id $userId"
Für Puristen ist die
Variante 4 -- diese nutzt ausschließlich
awk:
#!/bin/bash
userId=$UID
userName=$(awk -F : '/:'$userId':/ { print $1 }' /etc/passwd)
echo "the script runs as user $userName with UNIX id $userId"
Das das auch mit einer Skriptsprache wie Perl geht, zeigt das
Variante 5:
#!/usr/bin/perl -w
$username=$ENV{'LOGNAME'};
# read /etc/passwd
open(FH, "/etc/passwd") || die "Cannot open /etc/passwd: $!";
{
while(<FH>) {
# look for the user name
if (m/^${username}:/) {
@fields=split(/:/, $_);
# extract user id
$uid=$fields[2];
print "the script runs as user $username with UNIX id $uid\n";
}
}
}
close(FH);
Auswertung der Laufzeit
Nun wird es spannend, welche der fünf Varianten am flinkesten ist. Rufen Sie die Konstrukte alle einzeln auf, liefert time bei allen einen Wert von etwa 1s. Um die Unterschiede deutlicher sichtbar werden zu lassen, bauen wir eine Schleife drumherum und lassen das ganze 10000 Mal durchlaufen:
#!/bin/bash
# define loops
loops=10000
time {
for((i=$loops; i>0; i--))
do
userId=$UID
userName=$(whoami)
echo "the script runs as user $userName with UNIX id $userId"
done
} | grep "real"
Analog erfolgt das auch für die Varianten 2 bis 5. Folgendes Ergebnis wird sichtbar:
Variante |
Ausführungszeit |
Variante 1 mit whoami |
8.25s |
Variante 2 mit grep und cut |
11.03s |
Variante 3 mit grep und awk |
17.1s |
Variante 4 nur mit awk |
13.9s |
Variante 5 mit Perl |
15.22s |
Fazit
Eindeutiger Gewinner ist Variante 1, gefolgt Variante 2 und Variante 4. Es hilft also doch, bei der Programmierung stets die Laufzeit im Blick zu behalten.
Links