Ipython: Pérdida de memoria con% matplotlib en línea

Creado en 19 dic. 2014  ·  23Comentarios  ·  Fuente: ipython/ipython

Hola a todos

Encontré un problema. Simplemente inicie el código y mire la memoria. Luego elimine "% matplotlib inline" y ejecútelo nuevamente.

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

Comentario más útil

En segundo lugar, agradecería que se solucione este problema.

Todos 23 comentarios

También encontré este error, ¿hay alguna forma de obtener gráficos en línea sin pérdidas de memoria? No quiero iniciar procesos separados para cada trama, ya que las matrices son bastante grandes.

¿Puede comprobar esto cuando aumenta el uso de la memoria?

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

Esa es una lista donde se almacenan las cifras. Deberían estar allí solo temporalmente, pero tal vez se estén acumulando sin ser autorizados.

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

Por cierto, estoy trazando usando el método .plot() en marcos de datos de pandas.

Bien, mucho para esa teoría.

Es posible que los pandas también guarden algunos datos sobre las parcelas internamente. Sin embargo, el informe original no involucra a los pandas.

¿Cuánta memoria parece agregar cada trama adicional?

ok, este parece ser mi caso, estaba usando pandas 0.16.0, pero el problema está solucionado en el maestro:

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

Muchas gracias. Dejar abierto desde el informe original no involucró a los pandas.

Esto se puede reproducir de manera más simple:

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

Esto no pierde memoria, por lo que es algo en el lado de IPython, no en el lado de pyplot (creo).

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 Con su código de prueba, IPython en Windows 7 consume aquí aproximadamente 1,7 GB que no se liberan después. La ejecución con un número ligeramente superior de iteraciones provoca un error de memoria. Entonces esto sigue siendo un problema.

@asteppke ¿ El primer o segundo bloque?

@tacaswell Con su primer código de prueba ( %matplotlib inline ), el consumo de memoria aumenta a 1,7 GB. En contraste, cuando se usa la segunda pieza ( matplotlib.use('agg') ), el uso de memoria oscila solo entre 50 MB y 100 MB.

Ambas pruebas se ejecutan con Python 3.4 y la versión 4.0.5 del portátil IPython.

He jugado con esto un poco más. Me doy cuenta de que si vuelvo a ejecutar el ciclo for en el ejemplo de @tacaswell varias veces, el uso de la memoria no aumenta; parece que lo que importa es el número que crea en una sola celda. IPython ciertamente mantiene una lista de todas las cifras generadas en la celda para el backend en línea, pero esa lista definitivamente se borra después de que se ejecuta la celda, lo que no hace que el uso de la memoria disminuya, incluso después de hacer gc.collect() .

¿Podría nuestro código interactuar mal con algo en matplotlib? Pensé que _pylab_helpers.Gcf parecía probable, pero no parece que se aferre a nada.

Intenté tomar una referencia a una de las figuras y llamar gc.get_referrers() en ella; Aparte de la referencia que tenía en user_ns, todos los demás parecían objetos mpl; presumiblemente, muchos de ellos están en bucles de referencia. ¿Qué objeto es más probable al que otra cosa estaría manteniendo una referencia de manera inapropiada?

Dejaré esto en la 'lista de deseos' de hitos. Queremos solucionarlo, pero por el momento no estamos seguros de cómo avanzar más en la identificación del error, y no creo que valga la pena retrasar los lanzamientos.

Cualquiera que pueda progresar obtiene puntos brownie. También pastel.

No es realmente un progreso, pero la memoria parece estar perdida en algún lugar dentro del kernel. Tampoco gc.collect() llamar a summary.print_(summary.summarize(muppy.get_objects())) no encuentra nada de la memoria filtrada. Tampoco la configuración de todos los _N y _iN en None ayuda. Es realmente misterioso.

También me pregunté si estaba creando objetos incobrables, pero esos deberían terminar en gc.garbage cuando no hay otras referencias a ellos, y todavía está vacío cuando veo que usa mucha RAM.

Creo que alguien que sepa sobre estas cosas tendrá que usar herramientas de nivel C para rastrear qué memoria no se está liberando. No hay evidencia de que se mantengan vivos objetos Python adicionales en ningún lugar donde podamos encontrar.

En segundo lugar, agradecería que se solucione este problema.

Lo sabemos, pero en la actualidad nadie ha descubierto la causa del error.

+1

+1

Por cierto, sigo abordando este problema de vez en cuando en los últimos matplotlib, pandas, jupyter, ipython. Si alguien conoce algún depurador que pueda ayudar a solucionar este problema de comunicación multiproceso, hágamelo saber.

¿Quizás tenga algo que ver con el mecanismo de caché del navegador?

Buen pensamiento, pero no lo creo. Es el proceso de IPython que ocupa memoria extra, no el navegador, y
La reproducción de @tacaswell no implica el envío de gráficos al navegador.

Hola, creo que he encontrado parte del culpable y una manera de reducir este problema de manera significativa, pero no completamente.

Después de desplazarme por el código ipykernel/pylab/backend_inline.py , tuve la corazonada de que el modo interactivo almacena mucho "elementos de la trama", aunque no lo entiendo por completo, por lo que no puedo precisar la razón exacta con certeza.

Aquí está el código para verificar esto (basado en el fragmento de @tacaswell anterior), útil para cualquiera que intente implementar una solución.

Inicialización:

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

Prueba real:

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

Ejecutándolo durante 50 iteraciones del ciclo, obtengo:

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

Ejecutándolo durante 200 iteraciones del ciclo, obtengo:

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

que muestra el aumento casi lineal de la memoria con las iteraciones.

Ahora a la solución / solución alternativa: llame a matplotlib.interactive(False) antes del fragmento de prueba y luego ejecútelo.

Con 50 iteraciones:

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

Y con 200 iteraciones:

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

Lo que confirma que solo queda un aumento constante (independiente de las iteraciones).

Usando estos números, hago una estimación aproximada del tamaño de la fuga por iteración:

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

Y para una sola iteración del ciclo, obtengo 13560 . Por lo tanto, la cantidad de fuga por iteración es significativamente menor que el tamaño de la imagen, ya sea sin formato (> 3 MB) o comprimida en png (54 KB).

Además, extrañamente, ejecutar una prueba a pequeña escala (solo algunas iteraciones) repetidamente en la misma celda sin reiniciar el kernel es mucho menos consistente, no he podido entender esto ni determinar un patrón.

Espero que alguien con más conocimiento de los aspectos internos pueda tomarlo desde aquí, ya que no tengo el tiempo y el conocimiento para profundizar en él ahora mismo.

funciona

¿Fue útil esta página
0 / 5 - 0 calificaciones