2011-10-03 15 views
10

I recently asked a question su come salvare oggetti Python di grandi dimensioni su file. In precedenza mi ero imbattuto in problemi nella conversione di enormi dizionari Python in stringa e nella scrittura in file tramite write(). Ora sto usando pickle. Sebbene funzioni, i file sono incredibilmente grandi (> 5 GB). Ho poca esperienza nel campo di file così grandi. Volevo sapere se sarebbe stato più veloce, o persino possibile, comprimere questo file pickle prima di memorizzarlo in memoria.Il modo più veloce per archiviare file di grandi dimensioni in Python

+0

Memorizzarlo in memoria? – Cameron

+4

@JBernardo Che è come dire quando la macchina si guasta, puoi chiedere ai tuoi genitori un passaggio dove devi andare e poi prendere i mezzi pubblici per tornare ... –

+0

Memorizzarlo sul disco rigido. Suppongo che scrivere> 5 GB sull'hard disk richieda molto tempo. – puk

risposta

3

Il codice Python sarebbe estremamente lento quando si tratta dell'implementazione della serializzazione dei dati. Se provi a creare un equivalente a Pickle in Python puro, vedrai che sarà super lento. Fortunatamente i moduli integrati che funzionano sono abbastanza buoni.

Oltre al cPickle, troverete il modulo marshal, che è molto più veloce. Ma ha bisogno di un vero handle di file (non da un oggetto simile a un file). È possibile import marshal as Pickle e vedere la differenza. Non credo che si può fare un serializzatore personalizzato che è molto più veloce di questo ...

Ecco un vero e proprio (non tanto vecchio) serious benchmark of Python serializers

+0

Mi sento davvero stupido a chiederlo, ma perché è "serializzazione", e WOW è maresciallo davvero 100 volte più veloce di pickle? Posso passare facilmente al marshal o richiedere una revisione completa – puk

+0

Il dumping delle strutture dati in un flusso è * serializzazione *. Puoi facilmente passare a cPickle/marshal (pensavo che stavi usando cPickle). –

+0

no, stavo usando un pickle semplice (ppickle?). Ho appena fatto un semplice test comparando maresciallo e sottaceto e WOW, stiamo parlando di due ordini di grandezza. – puk

9

È possibile comprimere i dati con bzip2:

from __future__ import with_statement # Only for Python 2.5 
import bz2,json,contextlib 

hugeData = {'key': {'x': 1, 'y':2}} 
with contextlib.closing(bz2.BZ2File('data.json.bz2', 'wb')) as f: 
    json.dump(hugeData, f) 

carico in questo modo:

from __future__ import with_statement # Only for Python 2.5 
import bz2,json,contextlib 

with contextlib.closing(bz2.BZ2File('data.json.bz2', 'rb')) as f: 
    hugeData = json.load(f) 

È anche possibile comprimere i dati utilizzando zlib o gzip con più o meno la stessa interfaccia. Tuttavia, entrambi i tassi di compressione di zlib e gzip saranno inferiori a quelli ottenuti con bzip2 (o lzma).

+0

Non ho familiarità con questa sintassi, ma lo prendo BZ2File lo fa passare in memoria, quindi posso scaricarlo tramite json (o pickle sto assumendo) – puk

+0

@puk Con quale sintassi? L'istruzione 'with' è solo zucchero sintattico che assicura che il file sia chiuso. Puoi leggerlo come 'f = bz2.BZ2File (...)'. Questo ** non ** memorizzerà una quantità significativa di dati in memoria, e questa è una buona cosa. I dati saranno serializzati e compressi al volo, e l'utilizzo della memoria (meno cache del sistema operativo) non dovrebbe essere significativamente più grande di un MegaByte. – phihag

+0

l'approccio 'with' utilizza molta meno memoria, corretto? – puk

1

più veloce, o anche solo possibile, per comprimere il file salamoia prima del [la scrittura]

Certo che è possibile, ma non c'è alcun motivo per cercare di fare una copia zippata esplicita in memoria (potrebbe non fit) prima di scriverla, quando si può causare automaticamente di essere compresso come è scritto, con funzionalità built-in libreria standard;!)

Vedi http://docs.python.org/library/gzip.html. In sostanza, si crea un particolare tipo di flusso con

gzip.GzipFile("output file name", "wb") 

e poi usarlo esattamente come un normale file creata con open(...) (o file(...) per questo).

+0

Penso che sia quello che cercherò di fare, in pratica lo zip come è scritto – puk

-1

Guarda su Google ProtoBuffers. Anche se non sono progettati per file di grandi dimensioni, come i file audio-video, funzionano bene con la serializzazione degli oggetti come nel tuo caso, perché sono stati progettati per questo. La pratica mostra che un giorno potresti aver bisogno di aggiornare la struttura dei tuoi file, e ProtoBuffers lo gestirà. Inoltre, sono altamente ottimizzati per compressione e velocità. E tu non sei legato a Python, Java e C++ sono ben supportati.

1

Avevo appena spieghi la risposta di phihag.

Quando si tenta di serializzare un oggetto si avvicina alla dimensione della RAM, salamoia/cPickle dovrebbe essere evitato, dal momento che requires additional memory of 1-2 times the size of the object per serializzare. Questo è vero anche durante lo streaming su BZ2File. Nel mio caso ero addirittura a corto di spazio di swap.

Ma il problema con JSON (e analogamente con i file HDF come menzionato nell'articolo collegato) è che non può serializzare tuple, che nei miei dati sono usate come chiavi per dettare. Non c'è una grande soluzione per questo; il meglio che ho trovato è stato quello di convertire tuple in stringhe, che richiede un po 'di memoria, ma molto meno di pickle. Al giorno d'oggi, è anche possibile utilizzare the ujson library, che è molto più veloce della libreria json.

Per tuple composte da stringhe (richiede stringhe non contenere virgole):

import ujson as json 
from bz2 import BZ2File 

bigdata = { ('a','b','c') : 25, ('d','e') : 13 } 
bigdata = dict([(','.join(k), v) for k, v in bigdata.viewitems()]) 

f = BZ2File('filename.json.bz2',mode='wb') 
json.dump(bigdata,f) 
f.close() 

di ricomporre le tuple:

bigdata = dict([(tuple(k.split(',')),v) for k,v in bigdata.viewitems()]) 

alternativa, se ad esempio le chiavi sono 2-tuple di interi:

bigdata2 = { (1,2): 1.2, (2,3): 3.4} 
bigdata2 = dict([('%d,%d' % k, v) for k, v in bigdata2.viewitems()]) 
# ... save, load ... 
bigdata2 = dict([(tuple(map(int,k.split(','))),v) for k,v in bigdata2.viewitems()]) 

Un altro vantaggio di questo approccio rispetto salamoia è che JSON sembra comprimere significativamente migliore di sottaceti quando si utilizza la compressione bzip2.

Problemi correlati