Was ist in Python in Bezug auf Speichernutzung und CPU-Verbrauch effizienter - Wörterbuch oder Objekt?
Hintergrund: Ich muss eine große Datenmenge in Python laden. Ich habe ein Objekt erstellt, das nur ein Feldcontainer ist. Das Erstellen von 4M-Instanzen und das Einfügen in ein Wörterbuch dauerte etwa 10 Minuten und ~ 6 GB Speicher. Nachdem das Wörterbuch fertig ist, ist der Zugriff darauf ein Wimpernschlag.
Beispiel: Um die Leistung zu überprüfen, habe ich zwei einfache Programme geschrieben, die dasselbe tun - eines verwendet Objekte, das andere Wörterbuch:
Objekt (Ausführungszeit ~ 18sec):
class Obj(object):
def __init__(self, i):
self.i = i
self.l = []
all = {}
for i in range(1000000):
all[i] = Obj(i)
Wörterbuch (Ausführungszeit ~ 12sec):
all = {}
for i in range(1000000):
o = {}
o['i'] = i
o['l'] = []
all[i] = o
Frage: Mache ich etwas falsch oder ist das Wörterbuch nur schneller als das Objekt? Wenn das Wörterbuch tatsächlich eine bessere Leistung erbringt, kann jemand erklären, warum?
quelle
Antworten:
Haben Sie versucht, zu verwenden
__slots__
?Aus der Dokumentation :
Spart dies also Zeit und Speicher?
Vergleich der drei Ansätze auf meinem Computer:
test_slots.py:
test_obj.py:
test_dict.py:
test_namedtuple.py (unterstützt in 2.6):
Führen Sie einen Benchmark aus (mit CPython 2.5):
Verwenden von CPython 2.6.2, einschließlich des genannten Tupeltests:
Also ja (nicht wirklich überraschend), die Verwendung
__slots__
ist eine Leistungsoptimierung. Die Verwendung eines benannten Tupels hat eine ähnliche Leistung wie__slots__
.quelle
Der Attributzugriff in einem Objekt verwendet den Wörterbuchzugriff hinter den Kulissen. Durch die Verwendung des Attributzugriffs erhöhen Sie den Overhead. Außerdem entsteht im Objektfall ein zusätzlicher Overhead, z. B. aufgrund zusätzlicher Speicherzuweisungen und Codeausführung (z. B. der
__init__
Methode).Wenn
o
es sich bei Ihrem Code um eineObj
Instanz handelt,o.attr
entspricht dieso.__dict__['attr']
einem geringen zusätzlichen Aufwand.quelle
o.__dict__["attr"]
ist derjenige mit zusätzlichem Overhead, der einen zusätzlichen Bytecode op benötigt; obj.attr ist schneller. (Natürlich wird der Attributzugriff nicht langsamer sein als der Abonnementzugriff - es ist ein kritischer, stark optimierter Codepfad.)Haben Sie darüber nachgedacht, ein benanntes Tupel zu verwenden ? ( Link für Python 2.4 / 2.5 )
Dies ist die neue Standardmethode zur Darstellung strukturierter Daten, mit der Sie die Leistung eines Tupels und die Bequemlichkeit einer Klasse erzielen.
Der einzige Nachteil im Vergleich zu Wörterbüchern ist, dass Sie (wie bei Tupeln) nach der Erstellung keine Möglichkeit haben, Attribute zu ändern.
quelle
Hier ist eine Kopie der @ hughdbrown-Antwort für Python 3.6.1. Ich habe die Anzahl um das Fünffache erhöht und Code hinzugefügt, um den Speicherbedarf des Python-Prozesses am Ende jedes Laufs zu testen.
Bevor die Downvoter es tun, sollten Sie darauf hinweisen, dass diese Methode zum Zählen der Größe von Objekten nicht genau ist.
Und das sind meine Ergebnisse
Mein Fazit lautet:
quelle
Ergebnisse:
quelle
Es gibt keine Frage.
Sie haben Daten ohne andere Attribute (keine Methoden, nichts). Sie haben also einen Datencontainer (in diesem Fall ein Wörterbuch).
Normalerweise denke ich lieber in Datenmodellierung . Wenn es ein großes Leistungsproblem gibt, kann ich etwas in der Abstraktion aufgeben, aber nur mit sehr guten Gründen.
Bei der Programmierung geht es darum, die Komplexität zu verwalten, und die Aufrechterhaltung der richtigen Abstraktion ist sehr oft einer der nützlichsten Wege, um ein solches Ergebnis zu erzielen.
Über die Gründe, warum ein Objekt langsamer ist, denke ich, dass Ihre Messung nicht korrekt ist.
Sie führen zu wenig Zuweisungen innerhalb der for-Schleife aus, und daher sehen Sie dort die unterschiedliche Zeit, die erforderlich ist, um ein Diktat (intrinsisches Objekt) und ein "benutzerdefiniertes" Objekt zu instanziieren. Obwohl sie aus sprachlicher Sicht gleich sind, haben sie eine ganz andere Implementierung.
Danach sollte die Zuweisungszeit für beide nahezu gleich sein, da die Mitglieder am Ende in einem Wörterbuch verwaltet werden.
quelle
Es gibt noch eine andere Möglichkeit, die Speichernutzung zu reduzieren, wenn die Datenstruktur keine Referenzzyklen enthalten soll.
Vergleichen wir zwei Klassen:
und
Dies wurde möglich, da
structclass
-basierte Klassen die zyklische Speicherbereinigung nicht unterstützen, was in solchen Fällen nicht erforderlich ist.Es gibt auch einen Vorteil gegenüber einer
__slots__
Klasse auf Basis: Sie können zusätzliche Attribute hinzufügen:quelle
Hier sind meine Testläufe des sehr schönen Skripts von @ Jarrod-Chesney. Zum Vergleich führe ich es auch gegen Python2 aus, wobei "range" durch "xrange" ersetzt wird.
Aus Neugier habe ich zum Vergleich auch ähnliche Tests mit OrderedDict (ordict) hinzugefügt.
Python 3.6.9:
Python 2.7.15+:
In beiden Hauptversionen sehen die Schlussfolgerungen von @ Jarrod-Chesney immer noch gut aus.
quelle