2013-03-21 15 views
6

Uso una raccolta di set Python per archiviare oggetti unici. Ogni oggetto ha __hash__ e __eq__ sovrascritto.Python set molto grande. Come evitare l'eccezione di memoria insufficiente?

Il set contiene quasi 200 000 oggetti. Lo stesso set occupa quasi 4 GB di memoria. Funziona bene su una macchina con più di 5 GB, ma ora ho bisogno di eseguire lo script su una macchina con solo 3 GB di RAM disponibili.

Ho riscritto uno script in C# - in realtà ho letto gli stessi dati dalla stessa sorgente, lo ho messo in un analogo CLR del set (HashSet) e invece di 4 GB ci sono voluti quasi 350 MB mentre la velocità dell'esecuzione dello script era relativamente lo stesso (vicino a 40 secondi) Ma devo usare Python.

Q1: Python ha un set "disco persistente" o altre soluzioni? Immagino che possa memorizzare in memoria solo i dati "chiave" usati nei metodi hash/eq e tutto il resto può essere conservato su disco. O forse ci sono altre soluzioni in Python per avere una collezione unica di oggetti che potrebbe richiedere più memoria di quella disponibile nel sistema.

Q2: domanda meno pratica: perché python set richiede molta più memoria per un set?

Io uso standard di Python 2.7.3 su Ubuntu 12.10 a 64 bit

Grazie.

Update1: Cosa script:

  1. letto un sacco di documenti semi-strutturati JSON (ogni JSON consistono in oggetto serializzato con collezione di oggetti aggregati ad essa collegati)

  2. analizzare ogni Documento JSON per recuperare da esso l'oggetto principale e gli oggetti dalle raccolte aggregate. Ogni oggetto analizzato è memorizzato in un set. Set è usato per memorizzare solo oggetti unici. Per prima cosa ho usato un database, ma il vincolo univoco nel database funziona più lentamente x100-x1000. Ogni documento JSON viene analizzato per 1-8 diversi tipi di oggetto. Ogni tipo di oggetto è memorizzato nel proprio set per salvare in memoria solo oggetti univoci.

  3. Tutti i dati memorizzati nei set vengono salvati nel database relazionale con vincoli univoci. Ogni set è memorizzato in una tabella di database separata.

L'idea dello script di prendere dati non strutturati, rimuovere i duplicati provenienti da collezioni di oggetti aggregati nel documento JSON e memorizzare i dati strutturati al database relazionale.

Aggiornamento 2:

2 delnan: ho commentato tutte le righe di codice con l'aggiunta di un diverso set mantenendo tutto il personale (dati sempre, l'analisi, l'iterazione) lo stesso - Lo script ha 4 GB di memoria inferiore.

Significa che quando questi oggetti da 200 K vengono aggiunti ai set, iniziano a ricevere tanta memoria. L'oggetto è un semplice dato di film di TMDB - ID, una lista di generi, una lista di attori, registi, molti altri dettagli di film e probabilmente una descrizione di film di grandi dimensioni da Wikipedia.

+3

Perché non usi un database? –

+0

Che tipo di dati contiene l'oggetto? Potresti postare del codice? – TAS

+1

Si potrebbe provare a memorizzarli in [shelve] (http://docs.python.org/library/shelve.html), usando gli hash come chiavi. – georg

risposta

4

Imposta infatti utilizzano molta memoria, ma le liste non.

>>> from sys import getsizeof 
>>> a = range(100) 
>>> b = set(a) 
>>> getsizeof(a) 
872 
>>> getsizeof(b) 
8424 
>>> 

Se l'unica ragione per la quale si utilizza un set è quello di evitare i duplicati, io vi consiglio di utilizzare un elenco invece. Puoi evitare i duplicati testando se gli oggetti sono già presenti nella tua lista prima di aggiungerli. Potrebbe essere più lento rispetto all'utilizzo della meccanica integrata dei set, ma sicuramente impiegherebbe molta meno memoria.

2

Provare a utilizzare __slots__ per ridurre l'utilizzo della memoria.

Quando ho avuto questo problema con molti e molti oggetti, l'utilizzo di __slots__ riduce l'utilizzo della memoria a 1/3.

Ecco uno SO question about __slots__ che potresti trovare interessante.

5

L'approccio migliore sarebbe probabilmente quello di rendere più piccoli gli oggetti che si stanno memorizzando nell'insieme. Se contengono campi non necessari, rimuoverli.

per ridurre il sovraccarico oggetto generale si potrebbe anche usare __slots__ a dichiarare i campi utilizzati:

class Person(object): 
    __slots__ = ['name', 'age'] 
    def __init__(self): 
     self.name = 'jack' 
     self.age = 99 
+0

Assumendo CPython. PyPy esegue un'ottimizzazione simile per la maggior parte degli oggetti senza specificare IIRC '__slots__'. – delnan

Problemi correlati