- Benchmark-Ergebnisse, die Leistungskennzahlen für Operationen, Speicher und Ein-/Ausgabe in Python systematisch messen und die Laufzeit sowie den Speicherverbrauch einzelner Operationen quantifizieren
- Bei der Geschwindigkeit werden die relativen Latenzen verschiedener Operationen gezeigt, etwa 14ns für Attributzugriff, 29ns für das Anhängen an eine Liste, 9μs für das Öffnen einer Datei und 8,6μs für eine FastAPI-Antwort
- Beim Speicher werden konkrete Werte genannt, darunter 41 Byte für einen leeren String, 28 Byte für eine Ganzzahl, 56 Byte für eine leere Liste, 64 Byte für ein leeres Dictionary und 16MB für einen leeren Prozess
- Für Bereiche wie Datenstrukturen, Serialisierung und asynchrone Verarbeitung werden die Leistungsunterschiede zwischen der Standardbibliothek und alternativen Bibliotheken (
orjson, msgspec usw.) verglichen
- Als zentrale Erkenntnisse werden der hohe Speicher-Overhead von Python-Objekten, schnelle Lookups in dict/set, der speichersparende Effekt von
__slots__ und das Bewusstsein für den Overhead asynchroner Verarbeitung hervorgehoben
Überblick
- Eine Zusammenstellung von Leistungskennzahlen, die Python-Entwickler kennen sollten, mit real gemessenen Werten für Rechengeschwindigkeit und Speicherverbrauch
- Die Benchmarks wurden in einer Umgebung mit CPython 3.14.2 und Mac Mini M4 Pro (ARM, 14 Kerne, 24GB RAM) durchgeführt
- Die Ergebnisse legen den Schwerpunkt auf den relativen Vergleich, und Code sowie Daten sind in einem GitHub-Repository veröffentlicht
Speicherverbrauch (Memory Costs)
- Ein leerer Python-Prozess belegt 15,73MB Speicher
- Strings benötigen grundsätzlich 41 Byte plus 1 Byte pro Zeichen
- Beispiel: leerer String 41B, String mit 100 Zeichen 141B
- Zahlentypen: kleine Ganzzahlen (0–256) 28B, größere Ganzzahlen (1000) ebenfalls 28B, sehr große Ganzzahlen (10ⁱ⁰⁰) 72B, Fließkommazahlen 24B
- Kollektionen mit Grundgröße: Liste 56B, Dictionary 64B, Set 216B
- Bei 1.000 Einträgen: Liste 35,2KB, Dictionary 63,4KB, Set 59,6KB
- Klasseninstanzen: normale Klasse (5 Attribute) 694B,
__slots__-Klasse 212B
- Bei 1.000 Instanzen: normale Klasse 165,2KB,
__slots__-Klasse 79,1KB
Grundlegende Operationen (Basic Operations)
- Arithmetische Operationen: Ganzzahladdition 19ns, Fließkommaaddition 18,4ns, Ganzzahlmultiplikation 19,4ns
- String-Operationen: Konkatenation 39,1ns, f-string 64,9ns,
.format() 103ns, %-Formatierung 89,8ns
- Listenoperationen:
append() 28,7ns, List Comprehension (1.000 Elemente) 9,45μs, identische for-Schleife 11,9μs
- List Comprehension ist etwa 26% schneller als eine for-Schleife
Zugriff auf Kollektionen und Iteration (Collection Access and Iteration)
- Schlüssel-/Indexzugriff: Dictionary-Lookup 21,9ns, Set-Membership 19ns, Listenindexzugriff 17,6ns
- Listen-Membership (1.000 Elemente) liegt bei 3,85μs und ist damit etwa 200-mal langsamer als Set/Dictionary
- Längenprüfung:
len() benötigt bei Listen 18,8ns, bei Dictionaries 17,6ns und bei Sets 18ns
- Iteration: Liste (1.000 Elemente) 7,87μs, Dictionary 8,74μs,
sum() 1,87μs
Klassen und Attribute (Class and Object Attributes)
- Geschwindigkeit des Attributzugriffs: Sowohl normale Klassen als auch
__slots__-Klassen benötigen 14,1ns für Lesen und etwa 16ns für Schreiben
- Weitere Operationen:
@property lesen 19ns, getattr() 13,8ns, hasattr() 23,8ns
- Bei Verwendung von
__slots__ ist der Speicherspareffekt mehr als doppelt so groß, während die Zugriffsgeschwindigkeit auf ähnlichem Niveau bleibt
JSON und Serialisierung (JSON and Serialization)
- Leistung alternativer Bibliotheken gegenüber der Standardbibliothek
orjson serialisiert komplexe Objekte in 310ns und ist damit mehr als 8-mal schneller als json mit 2,65μs
msgspec erreicht 445ns, ujson 1,64μs
- Auch bei der Deserialisierung ist
orjson mit 839ns am schnellsten
- Pydantic:
model_dump_json() 1,54μs, model_validate_json() 2,99μs
Web-Frameworks (Web Frameworks)
- Bei derselben JSON-Antwort liegen FastAPI bei 8,63μs, Starlette bei 8,01μs, Litestar bei 8,19μs, Flask bei 16,5μs und Django bei 18,1μs
- FastAPI antwortet damit etwa doppelt so schnell wie Django
Datei-Ein-/Ausgabe (File I/O)
- Datei öffnen und schließen 9,05μs, 1KB lesen 10μs, 1MB lesen 33,6μs
- Schreiben: 1KB 35,1μs, 1MB 207μs
- Pickle ist sowohl beim Serialisieren als auch beim Deserialisieren etwa doppelt so schnell wie
json (pickle.dumps() 1,3μs, json.dumps() 2,72μs)
Datenbanken und Persistenz (Database and Persistence)
- SQLite: insert 192μs, select 3,57μs, update 5,22μs
- diskcache: set 23,9μs, get 4,25μs
- MongoDB: insert 119μs, find_one 121μs
- SQLite ist beim Lesen am schnellsten, während diskcache eine starke Schreibleistung bietet
Funktionsaufrufe und Ausnahmen (Function and Call Overhead)
- Funktionsaufrufe: leere Funktion 22,4ns, Methode 23,3ns, lambda 19,7ns
- Ausnahmebehandlung: try/except (Normalfall) 21,5ns, bei ausgelöster Ausnahme 139ns
- Typprüfung:
isinstance() 18,3ns, type()-Vergleich 21,8ns
Async-Overhead (Async Overhead)
- Coroutine-Erzeugung 47ns,
run_until_complete 27,6μs
asyncio.sleep(0) 39,4μs, gather(10 coroutines) 55μs
- Im Vergleich zu einem synchronen Funktionsaufruf (20ns) ist asynchrone Ausführung (28μs) etwa 1.000-mal langsamer
Zentrale Erkenntnisse (Key Takeaways)
- Der Speicher-Overhead von Python-Objekten ist hoch; selbst eine leere Liste benötigt 56 Byte
- Dictionary-/Set-Lookups sind um Hunderte Male schneller als die Suche in Listen
- Alternative JSON-Bibliotheken wie
orjson und msgspec sind 3- bis 8-mal schneller als der Standard
- Asynchrone Verarbeitung hat hohen Overhead und sollte nur genutzt werden, wenn Parallelität tatsächlich nötig ist
__slots__ kann den Speicher auf weniger als die Hälfte reduzieren, fast ohne Leistungsverlust
Noch keine Kommentare.