Überprüfung der Performance von Funktionen in Python mit timeit
In einem meiner letzten Blogartikel wurde ich von Alex in den Kommentaren auf eine mögliche Verbesserung hingewiesen.
Und zwar ging es um den folgenden Codeschnipsel, mit welchem ich den Unix-Timestamp der vollen Stunde, bei Angabe eines beliebigen Timestamps, berechnet habe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import time import datetime timestamp = '1387965457' # der Original-Timestamp datetimetuple = datetime.datetime.fromtimestamp(int(timestamp)) # Umwandeln in ein datetime-Objekt print datetimetuple datetime_fullhour = datetime.datetime( # Erstellen eines neuen datetime-Objekts, in welchem Minuten und Sekunden genullt sind datetimetuple.year, datetimetuple.month, datetimetuple.day, datetimetuple.hour) print datetime_fullhour timestamp_fullhour = int(time.mktime(datetime_fullhour.timetuple())) # Umwandeln des neuen Objekts in einen Unix-Timestamp print timestamp_fullhour |
Der Code von Alex sieht hingegeben bedeutend einfacher aus:
1 | fullhour = timestamp - (timestamp % (60*60)) |
Zur Überprüfung, ob beide Herangehensweisen wirklich das selbe Ergebnis liefern, habe ich den Code entsprechend angepasst:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import datetime import time timestamp = '1387965457' # der Original-Timestamp def test1(): datetimetuple = datetime.datetime.fromtimestamp(int(timestamp)) # Umwandeln in ein datetime-Objekt datetime_fullhour = datetime.datetime( # Erstellen eines neuen datetime-Objekts, in welchem Minuten und Sekunden genullt sind datetimetuple.year, datetimetuple.month, datetimetuple.day, datetimetuple.hour) timestamp_fullhour = int(time.mktime(datetime_fullhour.timetuple())) # Umwandeln des neuen Objekts in einen Unix-Timestamp return timestamp_fullhour def test2(): fullhour = int(timestamp) - (int(timestamp) % (60*60)) return fullhour print test1() print test2() |
Wie sich herausstellte, liefern beide Funktionen das gleiche Ergebnis. In der Diskussion kam jedoch noch die Vermutung auf, dass der Code von Alex ggf. schneller sein könnte, da weniger Module und auch weniger Objekt-Instanzen benötigt werden. Um dies zu prüfen, habe ich den Quelltext umgeschrieben und das Python-Module ‚timeit‘ verwendet. Damit sah der Code wie folgt aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import datetime import time import timeit timestamp = '1387965457' # der Original-Timestamp def test1(): datetimetuple = datetime.datetime.fromtimestamp(int(timestamp)) # Umwandeln in ein datetime-Objekt datetime_fullhour = datetime.datetime( # Erstellen eines neuen datetime-Objekts, in welchem Minuten und Sekunden genullt sind datetimetuple.year, datetimetuple.month, datetimetuple.day, datetimetuple.hour) timestamp_fullhour = int(time.mktime(datetime_fullhour.timetuple())) # Umwandeln des neuen Objekts in einen Unix-Timestamp return timestamp_fullhour def test2(): fullhour = int(timestamp) - (int(timestamp) % (60*60)) return fullhour print timeit.timeit(test1, number=10000) print timeit.timeit(test2, number=10000) |
Das Ergebnis zeigte, dass mein ursprünglicher Code fast 4x langsamer war, als der von Alex. Um noch einen drauf zusetzen, habe ich den timestamp nicht als string sondern als integer deklariert und die Umwandlung von string zu integer (mittels int()) entfernt. Das Ergebnis überraschte! Durch diese, doch sehr kleine, Änderung unterschied sich die Performance beider Funktionen um Faktor 20.
Weitere Erklärungen und die Ergebnisse des Benchmarkings, könnt Ihr im folgenden Video sehen:
Über weitere Empfehlungen, gerade in Bezug auf timeit, freue ich mich!
Neueste Kommentare