2009-08-22 14 views
185

Ho scritto un programma Python che agisce su un grande file di input per creare alcuni milioni di oggetti che rappresentano triangoli. L'algoritmo è:Come posso liberare esplicitamente memoria in Python?

  1. leggere un file di input
  2. processo il file e creare un elenco di triangoli, rappresentati dai loro vertici
  3. uscita i vertici nel formato OFF: una lista di vertici seguita da un elenco di triangoli. I triangoli sono rappresentati da indici nella lista dei vertici

Il requisito di OFF che stampo la lista completa dei vertici prima di stampare i triangoli significa che devo tenere l'elenco dei triangoli in memoria prima di I scrivi l'output in un file. Nel frattempo sto ricevendo errori di memoria a causa delle dimensioni degli elenchi.

Qual è il modo migliore per dire a Python che non ho più bisogno di alcuni dati e che può essere liberato?

+5

Perché non stampare i triangoli ad un file intermedio, e leggere di nuovo di nuovo quando ne hai bisogno? –

risposta

169

Secondo Python Official Documentation, è possibile forzare il garbage collector per liberare la memoria senza riferimenti con gc.collect(). Esempio:

import gc 
gc.collect() 
+8

Spesso le cose vengono raccolte con i rifiuti, tranne che in alcuni casi insoliti, quindi non credo che ciò possa aiutare molto. –

+4

Forse non abbastanza frequentemente. Può creare questi "milioni di oggetti" in materia di millisecondi. – Havenard

+2

Sì, ma il garbage collector viene eseguito ogni 700 volte quando si crea un oggetto, quindi verrà eseguito migliaia di volte durante quei millisecondi. –

18

Python viene sottoposto a raccolta dati inutili, quindi se si riducono le dimensioni dell'elenco, si recupera la memoria. È inoltre possibile utilizzare l'istruzione "del" per sbarazzarsi di una variabile del tutto:

biglist = [blah,blah,blah] 
#... 
del biglist 
+8

Questo è e non è vero. Mentre la riduzione della dimensione dell'elenco consente di recuperare la memoria, non vi è alcuna garanzia quando ciò accadrà. – user142350

+1

No, ma di solito sarà d'aiuto. Tuttavia, come ho capito la domanda qui, il problema è che deve avere così tanti oggetti che esaurisce la memoria prima di elaborarli tutti, se li legge in una lista. Cancellare la lista prima che sia terminata l'elaborazione è improbabile che sia una soluzione utile. ;) –

+1

Si noti inoltre che del non garantisce che un oggetto verrà eliminato. Se ci sono altri riferimenti all'oggetto, non sarà liberato. –

24

La dichiarazione del potrebbe essere utile, ma IIRC non è garantito per liberare la memoria. Il docs are here ... e un why it isn't released is here.

Ho sentito persone su sistemi Linux e Unix che forgiavano un processo Python per fare un po 'di lavoro, ottenere risultati e poi ucciderlo.

This article ha note sul garbage collector Python, ma penso mancanza di controllo della memoria è l'aspetto negativo di memoria gestita

+0

IronPython e Jython sarebbero un'altra opzione per evitare questo problema? – voyager

+0

@voyager: no, non lo farebbe. E nessuna altra lingua, in realtà. Il problema è che legge grandi quantità di dati in una lista e che i dati sono troppo grandi per la memoria. –

+0

Probabilmente sarebbe * peggio * con IronPython o Jython. In quegli ambienti, non ti è nemmeno garantito che la memoria verrà rilasciata se nient'altro ha un riferimento. –

14

Non è possibile la memoria in modo esplicito libero. Quello che devi fare è assicurarti di non mantenere riferimenti agli oggetti. Verranno quindi raccolti dei rifiuti, liberando la memoria.

Nel tuo caso, quando hai bisogno di elenchi di grandi dimensioni, in genere devi riorganizzare il codice, in genere utilizzando generatori/iteratori. In questo modo non è necessario avere in memoria gli elenchi di grandi dimensioni.

http://www.prasannatech.net/2009/07/introduction-python-generators.html

+1

Se questo approccio è fattibile, probabilmente vale la pena farlo. Ma va notato che non si può fare un accesso casuale agli iteratori, che potrebbe causare problemi. –

+0

È vero, e se ciò è necessario, è probabile che l'accesso a set di dati di grandi dimensioni casualmente richieda una sorta di database. –

+0

È possibile utilizzare facilmente un iteratore per estrarre un sottoinsieme casuale di un altro iteratore. –

83

Purtroppo (a seconda della versione e il rilascio di Python) alcuni tipi di oggetti usano "liste libere", che sono un'ottimizzazione locale pulito, ma possono causare la frammentazione di memoria, in particolare rendendo più e più memoria "destinato "solo per oggetti di un certo tipo e quindi non disponibili per il" fondo generale ".

L'unico modo veramente affidabile per garantire che un utilizzo grande ma temporaneo della memoria restituisca tutte le risorse al sistema quando è terminato, è che l'utilizzo avvenga in un sottoprocesso, quindi il lavoro affamato di memoria termina. In tali condizioni, il sistema operativo eseguirà il proprio lavoro e riciclerà volentieri tutte le risorse che il sottoprocesso potrebbe aver inghiottito.Fortunatamente, il modulo multiprocessing rende questo tipo di operazione (che era piuttosto un dolore) non troppo male nelle versioni moderne di Python.

Nel tuo caso di utilizzo, sembra che il modo migliore per i sottoprocessi di accumulare alcuni risultati e tuttavia garantire che tali risultati siano disponibili per il processo principale consiste nell'utilizzare i file semi-temporanei (per mezzo temporaneo intendo, NON il tipo di file che spariscono automaticamente quando sono chiusi, solo file ordinari che si eliminano in modo esplicito quando si ha finito con essi).

+18

Mi piacerebbe sicuramente vedere un esempio banale di questo. –

+3

Seriamente. Cosa ha detto @AaronHall. –

+10

@AaronHall Esempio banale [ora disponibile] (http://stackoverflow.com/a/24126616/1600898), utilizzando 'multiprocessing.Manager' anziché i file per implementare lo stato condiviso. – user4815162342

2

Se non ti interessa il riutilizzo dei vertici, potresti avere due file di output, uno per i vertici e uno per i triangoli. Quindi aggiungi il file triangolo al file vertice quando hai finito.

+0

I figura I può mantenere solo i vertici in memoria e stampare i triangoli in un file, quindi stampare i vertici solo alla fine. Tuttavia, l'atto di scrivere i triangoli in un file è un enorme drenaggio delle prestazioni. C'è un modo per accelerare * quello * su? –

6

Altri hanno pubblicato alcuni modi in cui si potrebbe essere in grado di "convincere" l'interprete Python a liberare la memoria (o altrimenti evitare di avere problemi di memoria). Probabilmente dovresti provare prima le loro idee. Tuttavia, ritengo importante darti una risposta diretta alla tua domanda.

Non c'è davvero alcun modo per dire direttamente a Python di liberare memoria. Il fatto è che se si desidera un livello di controllo basso, si dovrà scrivere un'estensione in C o C++.

Detto questo, ci sono alcuni strumenti per aiutare con questo:.

+0

gc.collect() e del gc.garbage [:] funzionano bene quando sto usando grandi quantità di memoria –

12

(liberando la memoria viene periodicamente fatto automaticamente del può essere così la vostra amico, poiché contrassegna gli oggetti come cancellabili.)

Forse non dovresti incorrere in alcun problema di memoria, in primo luogo utilizzando una struttura più compatta per i tuoi dati. Pertanto, gli elenchi di numeri sono molto meno efficienti in termini di memoria rispetto al formato utilizzato dal modulo standard array o dal modulo numpy di terze parti. Dovresti risparmiare memoria inserendo i tuoi vertici in una matrice NumPy 3xN e i tuoi triangoli in una matrice di elementi N.

3

Ho avuto un problema simile nella lettura di un grafico da un file. L'elaborazione ha incluso il calcolo di una matrice float 200 000x200 000 (una riga alla volta) che non si adattava alla memoria. Cercando di liberare la memoria tra i calcoli usando gc.collect() risolto l'aspetto relativo alla memoria del problema ma si sono verificati problemi di prestazioni: non so perché ma anche se la quantità di memoria utilizzata è rimasta costante, ogni nuova chiamata a ha richiesto un po 'di più tempo rispetto al precedente. Quindi abbastanza rapidamente la raccolta dei rifiuti ha richiesto la maggior parte del tempo di calcolo.

Per risolvere entrambi i problemi di memoria e prestazioni, sono passato all'uso di un trucco multithreading che ho letto da qualche parte (mi dispiace, non riesco più a trovare il post correlato). Prima di leggere ogni riga del file in un grande ciclo for, elaborarlo ed eseguire gc.collect() una volta ogni tanto per liberare spazio nella memoria. Ora chiamo una funzione che legge ed elabora un blocco del file in una nuova discussione. Una volta terminato il thread, la memoria viene automaticamente liberata senza lo strano problema di prestazioni.

In pratica funziona così:

from dask import delayed // this module wraps the multithreading 
def f(storage, index, chunk_size): // the processing function 
    // read the chunk of size chunk_size starting at index in the file 
    // process it using data in storage if needed 
    // append data needed for further computations to storage 
    return storage 

partial_result = delayed([]) // put into the delayed() the constructor for your data structure 
// i personally use "delayed(nx.Graph())" since I am creating a networkx Graph 
chunk_size = 100 // ideally you want this as big as possible while still enabling the computations to fit in memory 
for index in range(0, len(file), chunk_size): 
    // we indicates to dask that we will want to apply f to the parameters partial_result, index, chunk_size 
    partial_result = delayed(f)(partial_result, index, chunk_size) 

    // no computations are done yet ! 
    // dask will spawn a thread to run f(partial_result, index, chunk_size) once we call partial_result.compute() 
    // passing the previous "partial_result" variable in the parameters assures a chunk will only be processed after the previous one is done 
    // it also allows you to use the results of the processing of the previous chunks in the file if needed 

// this launches all the computations 
result = partial_result.compute() 

// one thread is spawned for each "delayed" one at a time to compute its result 
// dask then closes the tread, which solves the memory freeing issue 
// the strange performance issue with gc.collect() is also avoided 
Problemi correlati