Ich habe ein Python-Programm geschrieben, das auf eine große Eingabedatei reagiert, um einige Millionen Objekte zu erstellen, die Dreiecke darstellen. Der Algorithmus lautet:
- Lesen Sie eine Eingabedatei
- Verarbeiten Sie die Datei und erstellen Sie eine Liste der Dreiecke, die durch ihre Scheitelpunkte dargestellt werden
- Geben Sie die Scheitelpunkte im AUS-Format aus: eine Liste der Scheitelpunkte, gefolgt von einer Liste der Dreiecke. Die Dreiecke werden durch Indizes in der Liste der Eckpunkte dargestellt
Die Anforderung von AUS, dass ich die vollständige Liste der Eckpunkte ausdrucken muss, bevor ich die Dreiecke drucke, bedeutet, dass ich die Liste der Dreiecke im Speicher halten muss, bevor ich die Ausgabe in eine Datei schreibe. In der Zwischenzeit erhalte ich aufgrund der Größe der Listen Speicherfehler.
Was ist der beste Weg, um Python mitzuteilen, dass ich einige Daten nicht mehr benötige und sie freigegeben werden können?
python
memory
memory-management
Nathan Fellman
quelle
quelle
Antworten:
Laut der offiziellen Python-Dokumentation können Sie den Garbage Collector zwingen, nicht referenzierten Speicher mit freizugeben
gc.collect()
. Beispiel:quelle
gc.collect()
Sie sich am Ende einer Schleife aufrufen, können Sie eine Fragmentierung des Speichers vermeiden, was wiederum dazu beiträgt, die Leistung aufrechtzuerhalten. Ich habe gesehen, dass dies einen signifikanten Unterschied macht (~ 20% Laufzeit IIRC)gc.collect()
nach dem Laden eines Pandas-Datenrahmens von HDF5 (500.000 Zeilen) reduzierte die Speichernutzung von 1,7 GB auf 500 MBdel my_array
gefolgt vongc.collect()
nach der Verarbeitung des Arrays ist die einzige Möglichkeit, den Speicher tatsächlich freizugeben, und mein Prozess überlebt, um das nächste Array zu laden.Leider (abhängig von Ihrer Version und Version von Python) verwenden einige Objekttypen "freie Listen", die eine ordentliche lokale Optimierung darstellen, jedoch zu einer Speicherfragmentierung führen können, insbesondere indem immer mehr Speicher nur für Objekte eines bestimmten Typs und "vorgesehen" wird damit für den "allgemeinen Fonds" nicht verfügbar.
Die einzige wirklich zuverlässige Möglichkeit, um sicherzustellen, dass eine große, aber vorübergehende Speichernutzung alle Ressourcen an das System zurückgibt, besteht darin, diese Nutzung in einem Unterprozess durchzuführen, der die speicherhungrige Arbeit erledigt und dann beendet. Unter solchen Bedingungen erledigt das Betriebssystem seine Aufgabe und recycelt gerne alle Ressourcen, die der Unterprozess möglicherweise verschlungen hat. Glücklicherweise
multiprocessing
macht das Modul diese Art von Operation (die früher eher schmerzhaft war) in modernen Versionen von Python nicht schlecht.In Ihrem Anwendungsfall scheint es der beste Weg für die Unterprozesse zu sein, einige Ergebnisse zu akkumulieren und dennoch sicherzustellen, dass diese Ergebnisse für den Hauptprozess verfügbar sind, die Verwendung von semi-temporären Dateien (mit semi-temporär meine ich, NICHT die Art von Dateien, die beim Schließen automatisch verschwinden, nur gewöhnliche Dateien, die Sie explizit löschen, wenn Sie damit fertig sind).
quelle
multiprocessing.Manager
anstelle von Dateien der gemeinsam genutzte Status implementiert wird.Die
del
Anweisung könnte von Nutzen sein, aber beim IIRC kann nicht garantiert werden, dass der Speicher freigegeben wird . Die Dokumente sind hier ... und warum es nicht veröffentlicht wird, finden Sie hier .Ich habe Leute auf Linux- und Unix-Systemen gehört, die einen Python-Prozess gezwungen haben, etwas zu arbeiten, Ergebnisse zu erzielen und ihn dann zu beenden.
Dieser Artikel enthält Hinweise zum Python-Garbage-Collector, aber ich denke, mangelnde Speicherkontrolle ist der Nachteil des verwalteten Speichers
quelle
Python wird durch Müll gesammelt. Wenn Sie also die Größe Ihrer Liste reduzieren, wird Speicherplatz zurückgewonnen. Sie können auch die Anweisung "del" verwenden, um eine Variable vollständig zu entfernen:
quelle
Sie können den Speicher nicht explizit freigeben. Sie müssen lediglich sicherstellen, dass keine Verweise auf Objekte vorhanden sind. Sie werden dann Müll gesammelt, wodurch der Speicher frei wird.
Wenn Sie in Ihrem Fall große Listen benötigen, müssen Sie den Code normalerweise neu organisieren und stattdessen Generatoren / Iteratoren verwenden. Auf diese Weise müssen Sie die großen Listen überhaupt nicht im Speicher haben.
http://www.prasannatech.net/2009/07/introduction-python-generators.html
quelle
(
del
Kann Ihr Freund sein, da Objekte als löschbar markiert werden, wenn keine anderen Verweise darauf vorhanden sind. Der CPython-Interpreter behält diesen Speicher häufig für die spätere Verwendung bei, sodass Ihr Betriebssystem den "freigegebenen" Speicher möglicherweise nicht sieht.)Möglicherweise würden Sie überhaupt nicht auf Speicherprobleme stoßen, wenn Sie eine kompaktere Struktur für Ihre Daten verwenden. Daher sind Listen mit Zahlen viel weniger speichereffizient als das Format, das vom Standardmodul
array
oder vomnumpy
Modul eines Drittanbieters verwendet wird. Sie würden Speicher sparen, indem Sie Ihre Scheitelpunkte in ein NumPy 3xN-Array und Ihre Dreiecke in ein N-Element-Array einfügen.quelle
del
tut nichts, was es nicht tun würde, allen Namen, die auf ein Objekt verweisen, einen anderen Wert zuzuweisen.del
Befreit den Speicher aus Sicht von Python, aber im Allgemeinen nicht aus Sicht der C-Laufzeitbibliothek oder des Betriebssystems. Referenzen: stackoverflow.com/a/32167625/4297 , effbot.org/pyfaq/… .del
ist gleichermaßen effektiv bei Exits-from-Scope, Neuzuweisungen usw.Ich hatte ein ähnliches Problem beim Lesen eines Diagramms aus einer Datei. Die Verarbeitung umfasste die Berechnung einer 200 000 x 200 000 Float-Matrix (zeilenweise), die nicht in den Speicher passte. Der Versuch, den Speicher zwischen den Berechnungen freizugeben,
gc.collect()
behebt den speicherbezogenen Aspekt des Problems, führte jedoch zu Leistungsproblemen: Ich weiß nicht warum, aber obwohl die Menge des verwendeten Speichers konstant blieb,gc.collect()
dauerte jeder neue Aufruf etwas länger als Der vorherige. Das Sammeln von Müll nahm also ziemlich schnell die meiste Rechenzeit in Anspruch.Um sowohl die Speicher- als auch die Leistungsprobleme zu beheben, habe ich auf die Verwendung eines Multithreading-Tricks umgestellt, den ich einmal irgendwo gelesen habe (es tut mir leid, ich kann den entsprechenden Beitrag nicht mehr finden). Bevor ich jede Zeile der Datei in einer großen
for
Schleife las , verarbeitete und abgc.collect()
und zu ausführte, um Speicherplatz freizugeben. Jetzt rufe ich eine Funktion auf, die einen Teil der Datei in einem neuen Thread liest und verarbeitet. Sobald der Thread endet, wird der Speicher automatisch ohne das seltsame Leistungsproblem freigegeben.Praktisch funktioniert es so:
quelle
Andere haben einige Möglichkeiten veröffentlicht, wie Sie den Python-Interpreter möglicherweise dazu bringen können, den Speicher freizugeben (oder auf andere Weise Speicherprobleme zu vermeiden). Wahrscheinlich sollten Sie zuerst ihre Ideen ausprobieren. Ich halte es jedoch für wichtig, Ihnen eine direkte Antwort auf Ihre Frage zu geben.
Es gibt keine Möglichkeit, Python direkt anzuweisen, Speicherplatz freizugeben. Tatsache ist, dass Sie eine Erweiterung in C oder C ++ schreiben müssen, wenn Sie ein so geringes Maß an Kontrolle wünschen.
Es gibt jedoch einige Tools, die dabei helfen können:
quelle
Wenn Sie sich nicht für die Wiederverwendung von Scheitelpunkten interessieren, können Sie zwei Ausgabedateien verwenden - eine für Scheitelpunkte und eine für Dreiecke. Fügen Sie dann die Dreiecksdatei an die Scheitelpunktdatei an, wenn Sie fertig sind.
quelle