Ipython: Speicherverlust mit% matplotlib inline

Erstellt am 19. Dez. 2014  ·  23Kommentare  ·  Quelle: ipython/ipython

Hallo allerseits

Ich habe ein Problem gefunden. Starten Sie einfach den Code und sehen Sie sich den Speicher an. Löschen Sie dann "% matplotlib inline" und starten Sie erneut.

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker

%matplotlib inline

import os
import sys
import StringIO
import urllib, base64

from matplotlib import rcParams

rcParams['figure.figsize'] = (24, 6)
rcParams['figure.dpi'] = 150

OUTPUT_FILENAME = "Asd"

def printHTML(html):
    with open(OUTPUT_FILENAME, "a") as outputFile: outputFile.write(html if type(html) == str else html.encode('utf8') )

def friendlyPlot():

    figure = plt.Figure()
    ax = plt.subplot2grid((1,2), (0,0))

    ax.plot( range(1000), range(1000) )


    #plt.show() 
    fig = plt.gcf()

    imgdata = StringIO.StringIO()
    fig.savefig(imgdata, format='png')
    imgdata.seek(0)  # rewind the data
    image = imgdata.buf.encode('base64').replace('\n', '')
    printHTML('<img src="data:image/png;base64,{0}" /><br />'.format(image))
    plt.close('all')
    imgdata.close()

open(OUTPUT_FILENAME, 'w').close()

for i in range(500):
    friendlyPlot()
bug matplotlib

Hilfreichster Kommentar

Ich werde zweitens sagen, dass eine Lösung für dieses Problem wünschenswert wäre.

Alle 23 Kommentare

Ich habe auch diesen Fehler festgestellt. Gibt es eine Möglichkeit, Inline-Plots ohne Speicherlecks zu erhalten? Ich möchte nicht für jeden Plot separate Prozesse starten, da die Arrays ziemlich groß sind.

Können Sie dies überprüfen, wenn die Speichernutzung zunimmt:

len(IPython.kernel.zmq.pylab.backend_inline.show._to_draw)

Das ist eine Liste, in der Zahlen gespeichert werden. Sie sollten nur vorübergehend dort sein, aber vielleicht bauen sie sich auf, ohne geklärt zu werden.

len (IPython.kernel.zmq.pylab.backend_inline.show._to_draw) = 0

Übrigens, ich zeichne mit der .plot() -Methode auf Pandas-Datenrahmen.

OK, soviel zu dieser Theorie.

Es ist möglich, dass Pandas einige Daten auch intern in Plots speichert. Der ursprüngliche Bericht enthält jedoch keine Pandas.

Wie viel Speicher scheint jeder zusätzliche Plot hinzuzufügen?

ok, das scheint mein Fall zu sein, ich habe Pandas 0.16.0 verwendet, aber das Problem ist im Master behoben:

https://github.com/pydata/pandas/pull/9814

Vielen Dank. Offen gelassen, da der ursprüngliche Bericht keine Pandas beinhaltete.

Dies kann einfacher reproduziert werden:

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker

%matplotlib inline

import os
import sys
import StringIO
import urllib, base64

from matplotlib import rcParams

rcParams['figure.figsize'] = (24, 6)
rcParams['figure.dpi'] = 150



def friendlyPlot():
    fig, ax = plt.subplots()
    ax.plot(range(1000))
    fig.savefig('tmp.png')
    plt.close('all')


for i in range(500):
    friendlyPlot()

Dadurch geht kein Speicher verloren, so dass es sich um etwas auf der IPython-Seite handelt, nicht auf der Pyplot-Seite (glaube ich).

import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt
import matplotlib.ticker



import os
import sys
import StringIO
import urllib, base64

from matplotlib import rcParams

rcParams['figure.figsize'] = (24, 6)
rcParams['figure.dpi'] = 150



def friendlyPlot():
    fig, ax = plt.subplots()
    ax.plot(range(1000))
    fig.savefig('tmp.png')
    plt.close('all')


for i in range(500):
    friendlyPlot()

@tacaswell Mit Ihrem

@asteppke Der erste oder zweite Block?

@tacaswell Mit Ihrem ersten %matplotlib inline ) steigt der Speicherverbrauch auf 1,7 GB. Im Gegensatz dazu schwankt die Speichernutzung bei Verwendung des zweiten Teils ( matplotlib.use('agg') ) nur zwischen 50 MB und 100 MB.

Beide Tests werden mit Python 3.4 und IPython Notebook Version 4.0.5 ausgeführt.

Ich habe ein bisschen mehr damit gespielt. Ich stelle fest , dass die Speichernutzung nicht zunimmt, wenn ich die for-Schleife in gc.collect() nicht sinkt.

Könnte unser Code schlecht mit etwas in matplotlib interagieren? Ich dachte, _pylab_helpers.Gcf sah wahrscheinlich aus, aber es scheint an nichts festzuhalten.

Ich habe versucht, einen Verweis auf eine der Figuren zu finden und gc.get_referrers() darauf zu nennen. Abgesehen von der Referenz, die ich in user_ns hatte, sahen alle anderen wie mpl-Objekte aus - vermutlich befinden sich viele von ihnen in Referenzschleifen. Auf welches Objekt würde sich höchstwahrscheinlich etwas anderes unangemessen beziehen?

Ich lasse dies auf die Meilenstein-Wunschliste fallen. Wir wollen das Problem beheben, sind uns aber derzeit nicht sicher, wie wir weitere Fortschritte bei der Identifizierung des Fehlers erzielen können, und ich denke nicht, dass es sich lohnt, Veröffentlichungen dafür aufzuhalten.

Wer Fortschritte machen kann, bekommt Brownie-Punkte. Auch Kuchen.

Kein wirklicher Fortschritt, aber der Speicher scheint irgendwo im Kernel verloren zu sein. Das Aufrufen von gc.collect() nach oder innerhalb der Schleife hilft auch nicht, und summary.print_(summary.summarize(muppy.get_objects())) findet keinen der durchgesickerten Speicher. Es hilft auch nicht, alle _N und _iN auf None zu setzen. Es ist wirklich mysteriös.

Ich habe mich auch gefragt, ob es uneinbringliche Objekte erstellt, aber diese sollten in gc.garbage enden, wenn es keine anderen Verweise auf sie gibt, und das ist immer noch leer, wenn ich sehe, dass es viel RAM verbraucht.

Ich denke, jemand, der über diese Dinge Bescheid weiß, muss C-Level-Tools verwenden, um herauszufinden, welcher Speicher nicht freigegeben wird. Es gibt keine Hinweise darauf, dass zusätzliche Python-Objekte irgendwo am Leben bleiben, wo wir sie finden können.

Ich werde zweitens sagen, dass eine Lösung für dieses Problem wünschenswert wäre.

Wir wissen, aber derzeit hat niemand die Ursache des Fehlers herausgefunden.

+1

+1

Übrigens, ich treffe dieses Problem immer noch von Zeit zu Zeit auf der neuesten Matplotlib, Pandas, Jupiter, Ipython. Wenn jemand einen Debugger kennt, der bei der Fehlerbehebung bei dieser Multiprozess-Kommunikation helfen kann, lassen Sie es mich bitte wissen.

Könnte es vielleicht etwas mit dem Browser-Cache-Mechanismus zu tun haben?

Guter Gedanke, aber ich denke nicht. Es ist der Prozess von IPython, der zusätzlichen Speicherplatz beansprucht, nicht der Browser und
Bei der Reproduktion von gesendet .

Hallo, ich glaube, ich habe einen Teil des Täters gefunden und einen Weg gefunden, dieses Problem signifikant, aber nicht vollständig zu reduzieren!

Nachdem ich durch den ipykernel/pylab/backend_inline.py -Code gescrollt hatte, bekam ich die Vermutung, dass im interaktiven Modus viele "Handlungssachen" gespeichert werden, obwohl ich es nicht vollständig verstehe, sodass ich den genauen Grund nicht genau bestimmen kann mit Sicherheit.

Hier ist der Code, um dies zu überprüfen (basierend auf dem obigen Snippet von

Initialisierung:

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker

%matplotlib inline

matplotlib.rcParams['figure.figsize'] = (24, 6)
matplotlib.rcParams['figure.dpi'] = 150

from resource import getrusage
from resource import RUSAGE_SELF

def friendlyPlot():
    fig, ax = plt.subplots()
    ax.plot(range(1000))
    fig.savefig('tmp.png')
    plt.close('all')

Aktueller Test:

print("before any:  {:7d} kB".format(getrusage(RUSAGE_SELF).ru_maxrss))
friendlyPlot()
print("before loop: {:7d} kB".format(getrusage(RUSAGE_SELF).ru_maxrss))
for i in range(50):
    friendlyPlot()
print("after loop:  {:7d} kB".format(getrusage(RUSAGE_SELF).ru_maxrss))
import gc ; gc.collect(2)
print("after gc:    {:7d} kB".format(getrusage(RUSAGE_SELF).ru_maxrss))

Wenn ich es für 50 Iterationen der Schleife laufen lasse, bekomme ich:

before any:    87708 kB
before loop:  106772 kB
after loop:   786668 kB
after gc:     786668 kB

Wenn ich es für 200 Iterationen der Schleife laufen lasse, bekomme ich:

before any:    87708 kB
before loop:  100492 kB
after loop:  2824316 kB
after gc:    2824540 kB

Dies zeigt die fast lineare Zunahme des Speichers mit Iterationen.

Nun zum Fix / Workaround: Rufen Sie vor dem Test-Snippet matplotlib.interactive(False) und führen Sie es dann aus.

Mit 50 Iterationen:

before any:    87048 kB
before loop:  104992 kB
after loop:   241604 kB
after gc:     241604 kB

Und mit 200 Iterationen:

before any:    87536 kB
before loop:  103104 kB
after loop:   239276 kB
after gc:     239276 kB

Dies bestätigt, dass nur noch ein konstanter Anstieg (unabhängig von Iterationen) übrig bleibt.

Mit diesen Zahlen mache ich eine grobe Schätzung der Leckgröße pro Iteration:

(786668-(241604 - 104992))/50   = 13001.12
(2824316-(241604 - 104992))/200 = 13438.52

Und für eine einzelne Iteration der Schleife bekomme ich 13560 . Die Leckmenge pro Iteration ist also erheblich kleiner als die Bildgröße, sei es roh (> 3 MB) oder png-komprimiert (54 KB).

Seltsamerweise ist es auch viel weniger konsistent, einen kleinen Test (nur wenige Iterationen) wiederholt in derselben Zelle durchzuführen, ohne den Kernel neu zu starten. Ich konnte dies nicht verstehen oder ein Muster bestimmen.

Ich hoffe, jemand mit mehr Wissen über die Interna kann es von hier aus übernehmen, da mir die Zeit und das Wissen fehlen, um jetzt tiefer in das Thema einzutauchen.

Es klappt

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen