Ipython: %matplotlibインラインでのメモリリーク

作成日 2014年12月19日  ·  23コメント  ·  ソース: ipython/ipython

こんにちは、みなさん

問題が見つかりました。 コードを起動してメモリを確認するだけです。 次に、「%matplotlib inline」を削除して、再起動します。

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

最も参考になるコメント

次に、この問題の修正をお願いします。

全てのコメント23件

私もこのバグに遭遇しましたが、メモリリークなしでインラインプロットを取得する方法はありますか? 配列が非常に大きいため、プロットごとに個別のプロセスを起動したくありません。

メモリ使用量が増加したときにこれを確認できますか?

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

それは数字が保存されているリストです。 彼らは一時的にそこにいるはずですが、おそらく彼らはクリアされることなく蓄積しています。

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

ところで、私はパンダのデータフレームで.plot()メソッドを使用してプロットしています。

OK、その理論についてはこれだけです。

パンダがプロット周辺のデータを内部的に保持している可能性もあります。 ただし、元のレポートにはパンダは含まれていません。

追加の各プロットはどのくらいのメモリを追加するように見えますか?

わかりました、これは私の場合のようです、私はパンダ0.16.0を使用していましたが、問題はマスターで修正されています:

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

まことにありがとうございます。 元のレポートにはパンダが含まれていなかったため、開いたままにしておきます。

これはより簡単に再現できます。

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()

これはメモリをリークしないので、pyplot側ではなくIPython側にあるものです(私は思います)。

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テストコードを使用すると、Windows 7のIPythonはここで約1.7GBを消費しますが、後で解放されません。 わずかに多い反復回数で実行すると、メモリエラーが発生します。 したがって、これはまだ問題です。

@asteppke最初または2番目のブロック?

@tacaswell最初のテストコード( %matplotlib inline )では、メモリ消費量は最大1.7GBになります。 対照的に、2番目の部分( matplotlib.use('agg') )を使用する場合、メモリ使用量は50MBから100MBの間でのみ変動します。

どちらのテストも、Python3.4およびIPythonNotebookバージョン4.0.5で実行されます。

私はこれでもう少し遊んだ。 @tacaswellの例でforループを数回再実行しても、メモリ使用量は増加しないことにgc.collect()実行した後でもメモリ使用量が低下することはありません。

私たちのコードはmatplotlibの何かとひどく相互作用している可能性がありますか? _pylab_helpers.Gcf可能性が高いと思いましたが、何も保持していないようです。

図の1つへの参照を取得して、その上でgc.get_referrers()を呼び出してみました。 user_nsにある参照を除いて、他のすべてはmplオブジェクトのように見えました-おそらくそれらの多くは参照ループにあります。 他の何かが不適切に参照を保持している可能性が最も高いのはどのオブジェクトですか?

これをマイルストーン「ウィッシュリスト」にドロップします。 修正したいのですが、現時点ではバグの特定をさらに進める方法がわからないため、リリースを保留する価値はないと思います。

進歩できる人は誰でもブラウニーポイントを獲得します。 また、ケーキ。

実際には進歩していませんが、メモリはカーネル内のどこかで失われているようです。 ループの後でまたはループ内でgc.collect()呼び出すこともヘルプではなく、 summary.print_(summary.summarize(muppy.get_objects()))はリークされたメモリを検出しません。 すべての_N_iNNone設定しても効果はありません。 本当に不思議です。

また、収集できないオブジェクトを作成しているのではないかと思いましたが、他に参照がない場合はgc.garbageになるはずであり、RAMの負荷を使い果たしているのを見るとまだ空です。

これらのことを知っている人は、Cレベルのツールを使用して、解放されていないメモリを追跡する必要があると思います。 余分なPythonオブジェクトがどこにでも残っているという証拠はありません。

次に、この問題の修正をお願いします。

私たちは知っていますが、現時点では誰もバグの原因を解明していません。

+1

+1

ところで、私はまだ時々最新のmatplotlib、pandas、jupyter、ipythonでこの問題にぶつかっています。 このマルチプロセス通信のトラブルシューティングに役立つデバッガーを誰かが知っている場合は、私に知らせてください。

おそらくブラウザのキャッシュメカニズムと関係があるのでしょうか?

良い考えですが、私はそうは思いません。 これは、ブラウザではなく、余分なメモリを消費するIPythonのプロセスです。
@tacaswellの複製には、ブラウザへのプロットの送信は含まれません。

こんにちは、私は犯人の一部と、この問題を完全ではありませんが大幅に減らす方法を見つけたと思います!

ipykernel/pylab/backend_inline.pyコードをスクロールした後、完全には理解していませんが、インタラクティブモードでは「プロットのもの」が大量に保存されるという予感がしました。そのため、正確な理由を特定することはできません。確信をもって。

これを検証するコード(上記の@tacaswellのスニペットに基づく)は、修正を実装しようとする人に役立ちます。

初期化:

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')

実際のテスト:

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))

ループを50回繰り返して実行すると、次のようになります。

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

ループを200回繰り返して実行すると、次のようになります。

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

これは、反復によるメモリのほぼ線形の増加を示しています。

次に、修正/回避策を示します。テストスニペットの前にmatplotlib.interactive(False)呼び出して、実行します。

50回の反復で:

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

そして200回の反復で:

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

これは、(反復に関係なく)一定の増加のみが残っていることを確認します。

これらの数値を使用して、反復ごとのリークサイズの概算を行います。

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

そして、ループを1回繰り返すと、 13560ます。 したがって、反復ごとのリークの量は、未加工(> 3MB)またはpng圧縮(54KB)のいずれであっても、画像サイズよりも大幅に小さくなります。

また、不思議なことに、カーネルを再起動せずに同じセルで小規模なテスト(数回の反復のみ)を繰り返し実行すると、一貫性が大幅に低下します。これを理解したり、パターンを特定したりすることはできません。

私は今、それを深く掘り下げる時間と知識が不足しているので、内部の知識がもっとある人がここからそれを取ることができることを願っています。

できます

このページは役に立ちましたか?
0 / 5 - 0 評価