Einleitung
Manchmal möchte man vielleicht die Ausführungszeit eines Codeschnipsels, oder einer Funktion messen. Wenn man z. B. mehrere Möglichkeiten hat, eine Kopie eines Listenobjekts in Python zu erstellen, möchte man sicher die schnellste Methode wählen.
Was kann man tun? Eine Möglichkeit besteht darin, das DateTime-Modul zu verwenden, um die Start- und Endzeit der Aufgabe zu erfassen. Dann berechnet man die Differenz zwischen den beiden Zeiten. Diese Methode ist jedoch nicht effizient, da Hintergrundprozesse die Berechnungen verfälschen können. Wie kann man also die Ausführungszeit eines Codeschnipsels effizient ermitteln?
Das Python-Modul timeit ist genau für diese Aufgabe konzipiert. Es ist nützlich für die Messung der Ausführungszeit von Codeschnipseln.
Was ist das timeit-Modul?
timeit ist ein in Python built-in Modul, mit dem man die Ausführungszeit eines Python-Codefragments ermitteln kann.
Syntax:
import timeit
timeit.timeit(stmt='pass', setup='pass', number=1000000, globals=None, timer=<built-in function perf_counter>)
stmt: It is the code snippet that you want to measure. The default value is ‘pass’
setup: It is usually the import statements that you don’t want to include in the code snippet when measuring the time.
timer: the timer object which you don’t have to worry about as it will be set automatically.
number: the number of times you want to execute the code snippet. The default value is 1,000,000!
globals: You can pass the global namespace globals() so that the code snippet can use all the variables from the globals namespace.
Beispiele
(1) Das folgende Beispiel verwendet eine einzeilige Anweisung als Codeschnipsel. Es berechnet das Quadrat aller Zahlen von 0 bis 50. Man erkennt, dass es etwa 13 Sekunden dauert, das Quadrat aller Zahlen von 0 bis 50 etwa 1 Million Mal zu berechnen.
>>> timeit.timeit(stmt="[i**2 for i in range(50)]")
13.41026023699851
Um die für einen einzelnen Durchlauf benötigte Zeit zu berechnen, muss man dem Parameter number die Zahl 1 übergeben oder 13 Sekunden durch 1 Million dividieren.
>>> timeit.timeit(stmt="[i**2 for i in range(50)]", number=1)
5.101700116938446e-05
(2) Es ist zu beachten, dass das Modul timeit in seinem eigenen Namensraum läuft. Im folgenden Beispiel wird versucht, die Quadratwurzel aller Zahlen von 0 bis 50 mit Hilfe des math-Moduls zu finden, und das math-Modul wird in den Hauptbereich importiert. Der folgende Code führt aber zu einem NameError. Der Name „math“ ist nicht definiert, da das Modul math nicht in den Bereich timeit importiert wurde.
import math
import timeit
code = """
squares = [math.sqrt(i) for i in range(50)]
"""
timeit.timeit(stmt=code)
Traceback (most recent call last):
...
NameError: name 'math' is not defined
Wie lässt sich das beheben? Natürlich kann man auch „import math“ als Teil dess Codeschnipsels einfügen. Das ist allerdings ungünstig, denn man möchte nur die Ausführung des Codes abschätzen, ohne die Import-Anweisung zu verwenden. Es gibt zwei Möglichkeiten: a) setup und b) globals Parameter.
a) Der setup-Parameter: Man kann die Import-Anweisung mit dem Setup-Parameter übergeben. Die Anweisungen im Setup-Parameter werden nur einmal ausgeführt und sind nicht Teil des Codeschnipsels.
>>> import timeit
>>> code = """
... squares = [math.sqrt(i) for i in range(50)]
... """
>>> timeit.timeit(stmt=code, setup='import math')
13.79983167699902
b) Der globals-Parameter: globals() ist die built-in Funktion, die alle Objekte zurückgibt, die Teil des globalen Moduls sind. Wenn man das math-Mmodul in den globalen Bereich importiert hat, übernimmt man globals() durch den globals-Parameter, und macht dadurch den import math für den Codeschnipsel verfügbar.
>>> import math
>>> import timeit
>>> code = """
... squares = [math.sqrt(i) for i in range(50)]
... """
>>> timeit.timeit(stmt=code, globals=globals())
13.466401154999403
Verfügbare Methoden im timeit-Modul
Neben der Methode timeit() gibt es noch 2 weitere wichtige Methoden, die im timeit-Modul verfügbar sind.
timeit.repeat(): Sie funktioniert fast genauso wie timeit.timeit(), der Hauptunterschied ist, dass sie timeit.timeit() n-mal wiederholt, wie im Parameter repeat angegeben.
>>> import timeit
>>> code = """
... squares = [math.sqrt(i) for i in range(50)]
... """
>>> timeit.repeat(stmt=code, setup='import math', repeat=3)
[13.705560974998662, 13.598127913999633, 13.575166331998844]
timeit.default_timer(): Der Standard-Timer ist immer time.perf_counter(). Er gibt den Wert (in Sekundenbruchteilen) eines Leistungszählers zurück.
>>> import timeit
>>> start_time = timeit.default_timer()
>>> code = """
... squares = [math.sqrt(i) for i in range(50)]
... """
>>> timeit.timeit(stmt=code, setup='import math')
>>> stop_time = timeit.default_timer()
>>> print("The execution time is:", stop_time - start_time)
The execution time is: 13.663992461999442