Caccia alle perdite di memoria nelle applicazioni Python

Wai Chee Yau

Follow

Feb 13, 2019 – 4 min read

A Zendesk usiamo Python un bel po’ per costruire prodotti di apprendimento automatico (ML). Uno dei problemi di prestazioni comuni che abbiamo incontrato con le applicazioni di apprendimento automatico sono le perdite di memoria e i picchi. Il codice Python viene solitamente eseguito all’interno di container tramite framework di elaborazione distribuita come Hadoop, Spark e AWS Batch. Ad ogni contenitore viene assegnata una quantità fissa di memoria. Una volta che l’esecuzione del codice supera il limite di memoria specificato, il contenitore terminerà a causa di errori di memoria esaurita.

Una soluzione rapida è quella di aumentare l’allocazione di memoria. Tuttavia questo può comportare uno spreco di risorse e influenzare la stabilità dei prodotti a causa di picchi di memoria imprevedibili. Le cause delle perdite di memoria possono includere:

  • lacciare grandi oggetti che non vengono rilasciati
  • cicli di riferimento all’interno del codice
  • le librerie sottostanti/estensioni che perdono memoria

Un esercizio utile è quello di tracciare il profilo dell’utilizzo della memoria delle applicazioni per ottenere una migliore comprensione dell’efficienza dello spazio del codice e dei pacchetti sottostanti utilizzati. Questo post copre:

  • profilare l’uso della memoria dell’applicazione nel tempo
  • come ispezionare l’uso della memoria in parti specifiche del programma
  • consigli per il debug dei problemi di memoria

Si può guardare l’uso della memoria che varia nel tempo durante l’esecuzione del codice Python usando il pacchetto memory-profile.

# install the required packages
pip install memory_profiler
pip install matplotlib# run the profiler to record the memory usage
# sample 0.1s by defaut
mprof run --include-children python fantastic_model_building_code.py# plot the recorded memory usage
mprof plot --output memory-profile.png

A. Profilo della memoria in funzione del tempo

L’opzione include-bambini includerà l’uso della memoria di qualsiasi processo figlio generato tramite il processo padre. Il grafico A mostra un processo iterativo di formazione del modello che causa l’aumento della memoria in cicli come lotti di dati di formazione che vengono elaborati. Gli oggetti vengono rilasciati una volta che la garbage collection entra in azione.

Se l’utilizzo della memoria è in costante crescita, c’è un potenziale problema di perdite di memoria. Ecco un esempio di script fittizio per illustrare questo problema.

B. Impronte di memoria che aumentano nel tempo

Un punto di interruzione del debugger può essere impostato quando l’uso della memoria supera una certa soglia usando l’opzione pdb-mmem che è utile per la risoluzione dei problemi.

Dump della memoria in un momento preciso

È importante capire il numero previsto di oggetti di grandi dimensioni nel programma e se devono essere duplicati e/o trasformati in formati diversi.

Per analizzare ulteriormente gli oggetti in memoria, si può creare un dump dell’heap durante certe linee di codice nel programma con muppy.

Esempio di riassunto dell’heap dump della memoria

Un’altra utile libreria di profilazione della memoria è objgraph che può generare grafici di oggetti per ispezionare il lineage degli oggetti.

Cercare un ciclo di feedback rapido

Un approccio utile è la creazione di un piccolo “test case” che esegue solo il codice di perdita di memoria in questione. Considerare l’utilizzo di un sottoinsieme dei dati campionati casualmente se i dati di input completi sono lunghi da eseguire.

Eseguire i compiti intensivi di memoria in processi separati

Python non rilascia necessariamente la memoria immediatamente al sistema operativo. Per assicurarsi che la memoria venga rilasciata dopo l’esecuzione di un pezzo di codice, questo deve essere eseguito in un processo separato. Questa pagina fornisce maggiori dettagli sulla garbage collection di Python.

Il debugger può aggiungere riferimenti agli oggetti

Se viene usato un debugger con breakpoint come pdb, qualsiasi oggetto creato e referenziato manualmente dal debugger rimarrà nel profilo della memoria. Questo può creare un falso senso di perdite di memoria in cui gli oggetti non vengono rilasciati in modo tempestivo.

Fate attenzione ai pacchetti che possono avere perdite

Alcune librerie Python potrebbero potenzialmente avere perdite di memoria. Ad esempio, pandas ha un bel po’ di problemi noti di perdite di memoria.

Buona caccia!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.