Das folgende minimale Beispiel zum Aufrufen einer Python-Funktion aus C ++ hat einen Speicherverlust auf meinem System:
script.py
:
main.cpp
:
kompiliert mit
%Vor%und renne mit valgrind
%Vor%erzeugt die folgende Zusammenfassung
%Vor% Ich verwende Linux Mint 18.2 Sonya
, g++ 5.4.0
, Python 3.5.2
und TensorFlow 1.4.1
.
Durch das Entfernen von import tensorflow
verschwindet das Leck. Ist das ein Fehler in TensorFlow oder habe ich etwas falsch gemacht? (Ich erwarte, dass letzteres wahr ist.)
Zusätzlich, wenn ich eine Keras Ebene in Python erstellen
%Vor%und führe den Aufruf von C ++ an Python wiederholt aus
%Vor%Der Speicherverbrauch der Anwendung wächst kontinuierlich während der Laufzeit.
Also ich denke, es gibt etwas grundsätzlich falsch mit der Art, wie ich die Python-Funktion von C ++ aufrufen, aber was ist das?
Es gibt zwei verschiedene Arten von "Speicherlecks" in Ihrer Frage.
Valgrind erzählt Ihnen von der ersten Art von Speicherlecks. Es ist jedoch ziemlich üblich, dass Python-Module Speicher "auslecken" - es sind meistens einige globale Variablen, die beim Laden des Moduls zugewiesen / initialisiert werden. Und weil das Modul in Python nur einmal geladen ist, ist das kein großes Problem.
Ein bekanntes Beispiel ist numpys PyArray_API
: Es muss über _import_array
initialisiert werden, wird dann nie gelöscht und bleibt im Speicher, bis der Python-Interpreter heruntergefahren wird.
Es ist also ein "Speicherleck" pro Design, man kann argumentieren, ob es ein gutes Design ist oder nicht, aber am Ende des Tages gibt es nichts, was man dagegen tun könnte.
Ich habe nicht genug Einblick in das Tensorflow-Modul, um die Orte zu lokalisieren, wo solche Speicherlecks passieren, aber ich bin mir ziemlich sicher, dass es nichts ist, worüber man sich Sorgen machen sollte.
Das zweite "Speicherleck" ist subtiler.
Sie können einen Anhaltspunkt erhalten, wenn Sie die Valgrind-Ausgabe für 10^4
und 10^5
Iterationen der Schleife vergleichen - es wird fast keinen Unterschied geben! Es gibt jedoch einen Unterschied im Spitzenspeicherverbrauch.
Anders als in C ++ hat Python einen Garbage Collector - Sie können also nicht wissen, wann genau ein Objekt zerstört wird. CPython verwendet Referenzzählung, wenn also ein Referenzzähler 0 erhält, wird das Objekt zerstört. Wenn es jedoch einen Zyklus von Referenzen gibt (zB Objekt A
enthält eine Referenz von Objekt B
und Objekt B
enthält eine Referenz von Objekt B
) ist es nicht so einfach: Der Garbage Collector muss durchlaufen werden alle Objekte, um solche nicht mehr verwendeten Zyklen zu finden.
Man könnte denken, dass keras.layers.Input
irgendwo einen solchen Zyklus hat (und das ist richtig), aber das ist nicht der Grund für dieses "Speicherleck", das man auch bei reinem Python beobachten kann.
Wir verwenden objgraph -Paket, um die Referenzen zu überprüfen, lassen Sie uns das folgende Python-Skript ausführen:
%Vor%und führen Sie es aus:
%Vor% Wir können folgendes sehen: Am Ende gibt es genau 1000
Tersors, das heißt, keines unserer erstellten Objekte wurde entsorgt!
Wenn wir uns die Kette ansehen, die ein Tensor-Objekt am Leben erhält (wurde mit objgraph.show_chain
erzeugt), so sehen wir:
Es gibt ein Tensorflow-Graph-Objekt, in dem alle Tensoren registriert sind und dort bis zur Sitzung verbleiben ist geschlossen.
Bisher ist die Theorie jedoch wiitter:
%Vor%noch die hier vorgeschlagene Lösung:
%Vor%hat für die aktuelle Tensorflow-Version funktioniert. Was wahrscheinlich ein Bug ist.
Kurz gesagt: Sie machen nichts falsch in Ihrem C ++ - Code, es gibt keine Speicherlecks, für die Sie verantwortlich sind. Tatsächlich würden Sie genau denselben Speicherverbrauch sehen, wenn Sie die Funktion foo
von einem reinen Python-Skript immer wieder aufrufen würden.
Alle angelegten Tensoren sind in einem Graph-Objekt registriert und werden nicht automatisch freigegeben, Sie müssen sie durch Schließen der Backend-Sitzung freigeben - was jedoch aufgrund eines Fehlers in der aktuellen Tensorflow-Version 1.4.0 nicht funktioniert.
Tags und Links python memory-leaks c++ tensorflow python-3.x