memoryerror - ¿Puedo encontrar la solicitud de asignación que causó mi error de memoria de Python?
python exception class (3)
Contexto
Mi pequeño script de Python usa una biblioteca para trabajar con datos relativamente grandes. El algoritmo estándar para esta tarea es un algoritmo de programación dinámica, por lo que, presumiblemente, la biblioteca "bajo el capó" asigna una gran matriz para realizar un seguimiento de los resultados parciales del DP. De hecho, cuando trato de darle una entrada bastante grande, inmediatamente da un MemoryError
.
Preferiblemente, sin profundizar en las profundidades de la biblioteca, quiero averiguar si vale la pena probar este algoritmo en una máquina diferente con más memoria, o intentar reducir un poco el tamaño de mi entrada, o si es una causa perdida para el Tamaño de los datos que estoy tratando de usar.
Pregunta
Cuando mi código de Python lanza un MemoryError
, ¿hay una forma "de arriba a abajo" para que investigue cuál fue el tamaño de la memoria que mi código intentó asignar, lo que causó el error, por ejemplo, al inspeccionar el objeto de error?
No se puede ver en la excepción MemoryError
, y la excepción se produce para cualquier situación en la que la asignación de memoria haya fallado, incluidas las partes internas de Python que no se conectan directamente al código que crea nuevas estructuras de datos de Python; algunos módulos crean bloqueos u otros objetos de soporte y esas operaciones pueden fallar debido a que la memoria se ha agotado.
Tampoco puede saber necesariamente cuánta memoria se necesitaría para que toda la operación sea exitosa. Si la biblioteca crea varias estructuras de datos a lo largo del curso de la operación, tratar de asignar memoria para una cadena utilizada como clave de diccionario podría ser la última gota, o podría estar copiando toda la estructura de datos existente para la mutación, o cualquier otra cosa, pero esto no dice nada sobre la cantidad de memoria que se necesitará, además, durante el resto del proceso.
Dicho esto, Python puede proporcionarle información detallada sobre qué asignaciones de memoria se están realizando, y cuándo y dónde, mediante el módulo tracemalloc
. Usando ese módulo y un enfoque experimental, podría estimar cuánta memoria necesitaría completar su conjunto de datos.
El truco es encontrar conjuntos de datos para los cuales se pueda completar el proceso. Querría encontrar conjuntos de datos de diferentes tamaños, y luego puede medir cuánta memoria requieren esas estructuras de datos. tracemalloc.take_snapshot()
crear instantáneas antes y después con tracemalloc.take_snapshot()
, comparar las diferencias y las statistics entre las instantáneas de esos conjuntos de datos, y tal vez pueda extrapolar de esa información la cantidad de memoria que necesitaría su conjunto de datos más grande. Depende, por supuesto, de la naturaleza de la operación y de los conjuntos de datos, pero si hay algún tipo de patrón, tracemalloc
es su mejor oportunidad para descubrirlo.
Parece que MemoryError
no se crea con ningún dato asociado:
def crash():
x = 32 * 10 ** 9
return ''a'' * x
try:
crash()
except MemoryError as e:
print(vars(e)) # prints: {}
Esto tiene sentido, ¿cómo podría ser si no queda memoria?
No creo que haya una salida fácil. Puede comenzar desde el MemoryError
causa el MemoryError
e investigar con un depurador o usar un generador de perfiles de memoria como Pyampler (o psutil como se sugiere en los comentarios).
Puede ver la asignación de memoria con Pyampler pero deberá agregar las declaraciones de depuración localmente en la biblioteca que está usando. Asumiendo un paquete estándar de PyPi, aquí están los pasos:
- Clone el paquete localmente.
2 Utilice el módulo de resumen de Pyampler. Coloque siguiendo dentro del método de recursión principal,
from pympler import summary
def data_intensive_method(data_xyz)
sum1 = summary.summarize(all_objects)
summary.print_(sum1)
...
- Ejecute
pip install -e .
para instalar el paquete editado localmente. - Ejecute su programa principal y verifique el uso de memoria en la consola en cada iteración.