всем привет
Я нашел проблему. Просто запустите код и посмотрите на память. Затем удалите «% 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()
Я тоже столкнулся с этой ошибкой, есть ли способ получить встроенные графики без утечек памяти? Я не хочу запускать отдельные процессы для каждого сюжета, так как массивы довольно большие.
Можете ли вы проверить это при увеличении использования памяти:
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, но проблема исправлена в мастере:
Большое спасибо. Оставляем открытым, поскольку в исходном отчете панд не было.
Это можно воспроизвести проще:
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 КБ).
Кроме того, как ни странно, повторный запуск мелкомасштабного теста (всего несколько итераций) в одной и той же ячейке без перезапуска ядра гораздо менее согласован, я не смог понять это или определить шаблон.
Я надеюсь, что кто-то, обладающий более глубокими знаниями о внутреннем устройстве, сможет извлечь это отсюда, поскольку у меня нет времени и знаний, чтобы погрузиться в это глубже прямо сейчас.
оно работает
Самый полезный комментарий
Во-вторых, мы будем благодарны за исправление этой проблемы.