wake-up-neo.com

Wie kann ich in Python explizit Speicher freigeben?

Ich habe ein Programm Python geschrieben, das auf eine große Eingabedatei einwirkt, um einige Millionen Objekte zu erstellen, die Dreiecke darstellen. Der Algorithmus ist:

  1. eine Eingabedatei lesen
  2. verarbeiten Sie die Datei und erstellen Sie eine Liste von Dreiecken, die durch ihre Scheitelpunkte dargestellt werden
  3. ausgabe der Eckpunkte im OFF-Format: Eine Liste von Eckpunkten, gefolgt von einer Liste von Dreiecken. Die Dreiecke werden durch Indizes in der Liste der Eckpunkte dargestellt

Die Anforderung von OFF, dass ich die vollständige Liste der Eckpunkte ausdrucken muss, bevor ich die Dreiecke ausdrucken kann, bedeutet, dass ich die Liste der Dreiecke im Speicher halten muss, bevor ich die Ausgabe in eine Datei schreibe. In der Zwischenzeit erhalte ich Speicherfehler aufgrund der Größe der Listen.

Wie kann ich Python am besten mitteilen, dass ich einige Daten nicht mehr benötige und sie freigegeben werden können?

320
Nathan Fellman

Gemäß Python Official Documentation können Sie den Garbage Collector mit gc.collect() zwingen, nicht referenzierten Speicher freizugeben. Beispiel:

import gc
gc.collect()
350
Havenard

Leider (abhängig von Ihrer Version und Version von Python) verwenden einige Objekttypen "freie Listen", die eine saubere lokale Optimierung darstellen, aber möglicherweise eine Speicherfragmentierung verursachen, insbesondere indem immer mehr Speicher nur für Objekte eines bestimmten Typs und "zweckgebunden" wird dadurch nicht verfügbar für den "allgemeinen Fonds".

Die einzige wirklich zuverlässige Möglichkeit, um sicherzustellen, dass eine große, aber temporäre Speichernutzung alle Ressourcen an das System zurückgibt, besteht darin, diese Nutzung in einem Unterprozess durchzuführen, der die speicherhungrige Arbeit beendet. Unter solchen Umständen wird das Betriebssystem seine Aufgabe erfüllen und alle Ressourcen, die der Unterprozess möglicherweise verschlungen hat, gerne wiederverwenden. Glücklicherweise macht das multiprocessing -Modul diese Art von Operation (die früher ziemlich mühsam war) in modernen Versionen von Python nicht so schlimm.

In Ihrem Anwendungsfall scheint der beste Weg für die Unterprozesse, einige Ergebnisse zu akkumulieren und dennoch sicherzustellen, dass diese Ergebnisse für den Hauptprozess verfügbar sind, die Verwendung von semi-temporären Dateien zu sein (mit semi-temporär meine ich NICHT die Art von Dateien, die automatisch verschwinden, wenn geschlossen, nur normale Dateien, die Sie explizit löschen, wenn Sie mit ihnen fertig sind).

102
Alex Martelli

Die del -Anweisung könnte von Nutzen sein, aber IIRC garantiert nicht, dass der Speicher freigegeben wird . Die docs sind hier ... und ein warum es nicht veröffentlicht wird, ist hier .

Ich habe gehört, dass Leute auf Linux- und Unix-Systemen einen python -Prozess veranlasst haben, etwas zu arbeiten, Ergebnisse zu erzielen und ihn dann zu beenden.

Dieser Artikel hat Hinweise zum Garbage Collector Python, aber ich denke, mangelnde Speichersteuerung ist der Nachteil des verwalteten Speichers

41
Aiden Bell

Python wird mit Datenmüll gesammelt. Wenn Sie also die Größe Ihrer Liste verringern, wird Speicherplatz zurückgefordert. Sie können auch die Anweisung "del" verwenden, um eine Variable vollständig zu entfernen:

biglist = [blah,blah,blah]
#...
del biglist
29
Ned Batchelder

Sie können Speicher nicht explizit freigeben. Was Sie tun müssen, ist sicherzustellen, dass Sie keine Verweise auf Objekte behalten. Sie werden dann vom Müll gesammelt, wodurch der Speicher freigegeben wird.

Wenn Sie umfangreiche Listen benötigen, müssen Sie den Code in der Regel 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

22
Lennart Regebro

(del kann Ihr Freund sein, da Objekte als löschbar markiert werden, wenn keine anderen Verweise darauf vorhanden sind. Häufig speichert der CPython-Interpreter diesen Speicher für die spätere Verwendung, sodass Ihr Betriebssystem möglicherweise nicht die "freigegebenen" Objekte sieht. Erinnerung.)

Vielleicht stoßen Sie gar nicht erst auf ein Speicherproblem, wenn Sie eine kompaktere Struktur für Ihre Daten verwenden. Zahlenlisten sind daher viel weniger speichereffizient als das Format, das vom Standardmodul array oder vom Drittanbieter-Modul numpy verwendet wird. Sie würden Speicher sparen, indem Sie Ihre Eckpunkte in ein NumPy 3xN-Array und Ihre Dreiecke in ein N-Element-Array einfügen.

17
Eric O Lebigot

Andere haben einige Methoden veröffentlicht, mit denen 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, Speicher freizugeben. Tatsache ist, dass Sie eine Erweiterung in C oder C++ schreiben müssen, wenn Sie so wenig Kontrolle haben möchten.

Das heißt, es gibt einige Tools, die dabei helfen:

9
Jason Baker

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 (jeweils eine Zeile), die nicht in den Speicher passte. Der Versuch, den Speicher zwischen den Berechnungen mithilfe von gc.collect() freizugeben, behebt den speicherbezogenen Aspekt des Problems, führt jedoch zu Leistungsproblemen: Ich weiß nicht, warum, aber obwohl die Menge des verwendeten Speichers konstant blieb, wird jeder neue Aufruf von ausgeführt gc.collect() hat etwas länger gedauert als die vorherige. Das Sammeln des Mülls nahm also ziemlich schnell den größten Teil der Rechenzeit in Anspruch.

Um sowohl Speicher- als auch Leistungsprobleme zu beheben, habe ich auf einen Multithreading-Trick umgestellt, den ich irgendwo einmal gelesen habe (es tut mir leid, ich kann den zugehörigen Beitrag nicht mehr finden). Vorher habe ich jede Zeile der Datei in einer großen for Schleife gelesen, verarbeitet und ab und zu gc.collect() ausgeführt, 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 freigegeben, ohne dass ein merkwürdiges Leistungsproblem auftritt.

Praktisch funktioniert es so:

from dask import delayed  # this module wraps the multithreading
def f(storage, index, chunk_size):  # the processing function
    # read the chunk of size chunk_size starting at index in the file
    # process it using data in storage if needed
    # append data needed for further computations  to storage 
    return storage

partial_result = delayed([])  # put into the delayed() the constructor for your data structure
# I personally use "delayed(nx.Graph())" since I am creating a networkx Graph
chunk_size = 100  # ideally you want this as big as possible while still enabling the computations to fit in memory
for index in range(0, len(file), chunk_size):
    # we indicates to dask that we will want to apply f to the parameters partial_result, index, chunk_size
    partial_result = delayed(f)(partial_result, index, chunk_size)

    # no computations are done yet !
    # dask will spawn a thread to run f(partial_result, index, chunk_size) once we call partial_result.compute()
    # passing the previous "partial_result" variable in the parameters assures a chunk will only be processed after the previous one is done
    # it also allows you to use the results of the processing of the previous chunks in the file if needed

# this launches all the computations
result = partial_result.compute()

# one thread is spawned for each "delayed" one at a time to compute its result
# dask then closes the tread, which solves the memory freeing issue
# the strange performance issue with gc.collect() is also avoided
9
Retzod

Wenn Sie sich nicht für die Wiederverwendung von Scheitelpunkten interessieren, können Sie zwei Ausgabedateien haben - eine für Scheitelpunkte und eine für Dreiecke. Fügen Sie anschließend die Dreiecksdatei an die Scheitelpunktdatei an.

3
Nosredna