Ipython: تسرب الذاكرة مع٪ matplotlib مضمن

تم إنشاؤها على ١٩ ديسمبر ٢٠١٤  ·  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() على إطارات بيانات الباندا.

حسنًا ، كثيرًا لهذه النظرية.

من الممكن أن يحتفظ الباندا ببعض البيانات حول المؤامرات داخليًا أيضًا. لكن التقرير الأصلي لا يتضمن حيوانات الباندا.

ما مقدار الذاكرة التي يبدو أن كل قطعة إضافية تضيفها؟

حسنًا ، يبدو أن هذه هي حالتي ، كنت أستخدم الباندا 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()

هذا لا يؤدي إلى تسرب الذاكرة ، لذا فهو شيء على جانب IPython وليس جانب pyplot (على ما أعتقد).

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 مع كود الاختبار الخاص بك ، يستهلك IPython على Windows 7 هنا ما يقرب من 1.7 جيجا بايت والتي لم يتم تحريرها بعد ذلك. يؤدي التشغيل بعدد أكبر قليلاً من التكرارات إلى حدوث خطأ في الذاكرة. لذلك لا تزال هذه مشكلة.

asteppke الكتلة الأولى أم الثانية؟

tacaswell مع كود الاختبار الأول ( %matplotlib inline ) يرتفع استهلاك الذاكرة إلى 1.7 جيجا بايت. في المقابل ، عند استخدام القطعة الثانية ( matplotlib.use('agg') ) يتأرجح استخدام الذاكرة فقط بين 50 ميغا بايت و 100 ميغا بايت.

يتم تنفيذ كلا الاختبارين باستخدام Python 3.4 و IPython Notebook الإصدار 4.0.5.

لقد لعبت مع هذا أكثر قليلاً. لقد لاحظت أنه إذا أعدت تشغيل حلقة for في مثالtacaswell عدة مرات ، فلن يزيد استخدام الذاكرة - يبدو أن الرقم الذي تنشئه في خلية واحدة هو المهم. يحتفظ IPython بالتأكيد بقائمة بجميع الأرقام التي تم إنشاؤها في الخلية للواجهة الخلفية المضمنة ، ولكن يتم مسح هذه القائمة بالتأكيد بعد تشغيل الخلية ، مما لا يؤدي إلى انخفاض استخدام الذاكرة ، حتى بعد إجراء gc.collect() .

هل يمكن أن يتفاعل كودنا بشكل سيئ مع شيء ما في matplotlib؟ اعتقدت أن _pylab_helpers.Gcf يبدو مرجحًا ، لكن لا يبدو أنه يحتفظ بأي شيء.

حاولت الحصول على إشارة إلى أحد الأرقام والاتصال بـ gc.get_referrers() عليها ؛ بصرف النظر عن المرجع الذي كان لدي في user_ns ، بدت جميع الكائنات الأخرى مثل كائنات mpl - يفترض أن العديد منها في حلقات مرجعية. ما الشيء الذي من المرجح أن يكون هناك شيء آخر يحتفظ بالإشارة إليه بشكل غير لائق؟

أنا أسقط هذا إلى "قائمة الرغبات". نريد إصلاحه ، لكن في الوقت الحالي لسنا متأكدين من كيفية إحراز مزيد من التقدم في تحديد الخطأ ، ولا أعتقد أنه يستحق إيقاف الإصدارات الخاصة به.

أي شخص يمكنه إحراز تقدم يحصل على نقاط كعك. أيضا كعكة.

ليس تقدمًا حقًا ، ولكن يبدو أن الذاكرة ضاعت في مكان ما داخل النواة. كما لا يدعو gc.collect() بعد أو داخل مساعدة حلقة، و summary.print_(summary.summarize(muppy.get_objects())) لا يجد أي من تسرب الذاكرة. كما لم يتم تعيين كل المساعدة من _N و _iN إلى None . إنه أمر غامض حقًا.

تساءلت أيضًا عما إذا كان يتم إنشاء كائنات غير قابلة للتحصيل ، ولكن يجب أن ينتهي الأمر بها في gc.garbage عندما لا توجد مراجع أخرى لها ، ولا يزال هذا فارغًا عندما أراه يستخدم كميات كبيرة من ذاكرة الوصول العشوائي.

أعتقد أن أي شخص يعرف عن هذه الأشياء سيضطر إلى استخدام أدوات المستوى C لتعقب الذاكرة التي لا يتم تحريرها. لا يوجد دليل على بقاء كائنات بايثون إضافية على قيد الحياة في أي مكان يمكننا العثور عليه.

سأثني على أن إصلاح هذه المشكلة سيكون موضع تقدير.

نحن نعلم ، ولكن في الوقت الحالي لم يعمل أحد على معرفة سبب الخطأ.

+1

+1

راجع للشغل ، ما زلت أتعرض لهذه المشكلة من وقت لآخر على أحدث matplotlib ، الباندا ، 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

ولإعادة واحدة للحلقة ، أحصل على 13560 . لذا فإن مقدار التسرب لكل تكرار أصغر بكثير من حجم الصورة ، سواء كان خامًا (> 3 ميجابايت) أو مضغوطًا بتنسيق png (54 كيلوبايت).

ومن الغريب أيضًا أن إجراء اختبار على نطاق صغير (عدد قليل من التكرارات) مرارًا وتكرارًا في نفس الخلية دون إعادة تشغيل النواة هو أقل تناسقًا ، لم أتمكن من فهم هذا أو تحديد نمط.

آمل أن يتمكن شخص لديه معرفة أكبر بالأمور الداخلية من أخذها من هنا ، لأنني أفتقر إلى الوقت والمعرفة للتعمق فيها الآن.

إنها تعمل

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات