Um Laufzeit von Code in Python zu messen gibt es unter Anderem das Modul timeit. Im aktuellen Beitrag messen wir die Ausführungsgeschwindigkeit mehrerer mathematischer Funktionen.
Das Modul timeit kann sowohl in der Konsole, als auch in einem Skript verwendet werden. Im ersten Beispiel führen wir eine Berechnung in der Konsole durch. Nach Start der Python Shell geben wir folgenden Befehl ein:
python3 -m timeit "sum([x**2 for x in range(1,1001)])"
Nach dem Modulnamen timeit folgt ein String, der den zu messenden Code enthält. In unserem Fall berechnen wir alle Quadratzahlen von 1 bis 1000 und ermitteln deren Summe. Nach Ausführung erhalten wir folgende Ausgabe:
1000 loops, best of 3: 302 usec per loop
Unsere Berechnung wurde 1000 mal ausgeführt und anschließend wurde die schnellste Zeitmessung aus drei Wiederholungen ausgegeben. Über den Parameter -n und -r lassen sich die Parameter number und repeat verändern. Weitere Parameter sind hier beschrieben.
python3 -m timeit -n 1 -r 10 "sum([x**2 for x in range(1,1001)])"
Ausgabe:
1 loops, best of 10: 297 usec per loop
Somit können wir davon ausgehen, dass auf unserem System die gewünschte Berechnung circa 300 Mikrosekunden in Anspruch nimmt.
Als nächstes befassen wir uns mit der Verwendung von timeit in Skripten. Im ersten Schritt binden wir das Modul timeit ein. Anschließend implementieren wir die Funktion fakultaet(num). Diese berechnet die Fakultät einer Zahl und wir wollen sie mit der im math Modul vorhandenen Funktion factorial() vergleichen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import timeit def fakultaet(num): '''Funktion zur Berechnung der Fakultät''' # Negative Werte sind nicht definiert if num < 0: return 'Fehler fakultaet({}) nicht definiert'.format(num) # 0! = 1 if num == 0: return 1 # num! berechnen val = 1 for i in range(1, num+1): val *= i return val print("fakultaet(10000) hat {} Stellen".format(len(str(fakultaet(10000))))) |
Aufgrund der hohen Zahlen bei der Berechnung von Fakultäten geben wir für unseren ersten Test nur die Anzahl der Stellen aus:
fakultaet(10000) hat 35660 Stellen
Um timeit innerhalb von Skripten zu verwenden, muss der zu messende Code als String übergeben werden. Wir erstellen eine Kopie unserer Funktion und definieren sie als String namens TEST_FAKULTAET. Nun können wir unseren Performance Vergleich durchführen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
TEST_FAKULTAET = ''' def fakultaet(num): # Negative Werte sind nicht definiert if num < 0: return 'Fehler fakultaet({}) nicht definiert'.format(num) # 0! = 1 if num == 0: return 1 # num! berechnen val = 1 for i in range(1, num+1): val *= i return val fakultaet(10000) ''' # timeit direkt verwenden print(timeit.timeit(TEST_FAKULTAET, number=1)) print(timeit.timeit('math.factorial(10000)', setup='import math', number=1)) |
Die Funktion timeit.timeit() verfügt über fünf Parameter. Die Wichtigsten sind stmt (Code der gemessen werden soll), setup (Code, der einmalig zu Beginn ausgeführt wird) und number (Anzahl der Wiederholungen der Ausführung des Codes). Wir übergeben nun unseren erstellten String der Messfunktion und setzen number auf 1. Im Fall der Messung von factorial() müssen wir via setup zuerst das math Modul einbinden.
Ausgabe:
0.02177235599992855
0.002770686000076239
Die Messung ergibt, dass unsere Implementierung nicht besonders geglückt ist. Die Funktion factorial() aus dem math Modul ist deutlich schneller in der Ausführung.
Anstatt timeit direkt auszuführen, können wir auch die Timer Klasse verwenden. Wir speichern die Auszuführenden Berechnungen in den Objekten TIME1 und TIME2. Anschließend führen wir timeit() und repeat() auf diese Objekte aus. Im ersten Fall legen wir die Anzahl an Schleifendurchläufen fest, im Zweiten definieren wir auch die Wiederholungen für die Zeitmessung.
1 2 3 4 5 6 7 8 9 10 11 |
# timeit über die Timer Klasse verwenden TIME1 = timeit.Timer(TEST_FAKULTAET) TIME2 = timeit.Timer('math.factorial(10000)', setup='import math') # timeit auf Timerobjekt anwenden print(TIME1.timeit(number=1)) print(TIME2.timeit(number=1)) # repeat auf Timerobjekt anwenden print(TIME1.repeat(repeat=3, number=1)) print(TIME2.repeat(repeat=3, number=1)) |
Ausgabe:
0.02205854299972998
0.0028893759990751278
[0.024208838000049582, 0.02011976599897025, 0.020180663999781245]
[0.0027402919986343477, 0.002924312999311951, 0.0027616349998424994]
In den letzten beiden Zeilen erhalten wir jeweils drei Messergebnisse. Diese Anzahl kann mit einem anderen Wert für den Parameter repeat verändert werden. Da auf einem Computer zu jeder Zeit unterschiedliche Prozesse laufen, unterscheiden sich die Messergebnisse. Es lassen sich jedoch gute Schätzungen erreichen. Wer genauere Messungen benötigt, sollte sich mit dem Thema Profiling befassen.
Das komplette Beispiel herunterladen: python_timeit.zip
Hinterlasse jetzt einen Kommentar