Ipython: Утечка памяти с% matplotlib inline

Созданный на 19 дек. 2014  ·  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() на фреймах данных pandas.

Хорошо, хватит об этой теории.

Возможно, pandas также хранит некоторые данные вокруг графиков внутри. Однако в исходном отчете панд не упоминается.

Сколько памяти добавляет каждый дополнительный график?

хорошо, похоже, это мой случай, я использовал pandas 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 help. Это действительно загадочно.

Я также задавался вопросом, создает ли он не подлежащие сбору объекты, но они должны оказаться в gc.garbage когда на них нет других ссылок, и он все еще пуст, когда я вижу, что он использует много оперативной памяти.

Я думаю, что кому-то, кто знает об этих вещах, придется использовать инструменты уровня C, чтобы отслеживать, какая память не освобождается. Нет никаких доказательств того, что дополнительные объекты Python сохраняются где-либо, где бы мы ни находились.

Во-вторых, мы будем благодарны за исправление этой проблемы.

Мы знаем, но в настоящее время никто не выяснил причину ошибки.

+1

+1

Кстати, я все еще время от времени сталкиваюсь с этой проблемой на последних версиях matplotlib, pandas, jupyter, ipython. Если кто-то знает какой-либо отладчик, который может помочь устранить эту многопроцессорную связь, дайте мне знать.

Может быть, это как-то связано с механизмом кеширования браузера?

Хорошая мысль, но я так не думаю. Дополнительную память занимает процесс IPython, а не браузер, и
Воспроизведение @tacaswell не включает отправку графиков в браузер.

Привет, я считаю, что нашел часть виновника и способ значительно, но не полностью, уменьшить эту проблему!

После прокрутки кода ipykernel/pylab/backend_inline.py меня возникло подозрение, что интерактивный режим много хранит "сюжетные вещи", хотя я не совсем понимаю это, поэтому я не могу точно определить причину с уверенностью.

Вот код для проверки этого (на основе фрагмента

Инициализация:

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 рейтинги