2015-05-19 12 views
16

Ho uno script che genera numpyarray s bidirezionale con dtype=float e forma nell'ordine (1e3, 1e6). In questo momento sto usando np.save e np.load per eseguire operazioni IO con gli array. Tuttavia, queste funzioni richiedono diversi secondi per ciascun array. Ci sono metodi più veloci per salvare e caricare l'intero array (cioè senza fare ipotesi sul loro contenuto e ridurlo)? Sono aperto alla conversione di array in un altro tipo prima di salvare finché i dati vengono conservati esattamente.Le opzioni di salvataggio e caricamento più veloci per un array numpy

risposta

23

Per davvero grande array, ho sentito parlare di diverse soluzioni, e la maggior parte essi di essere pigro sul I/O:

  • NumPy.memmap, mappe grandi array di forma binaria
    • Pro:
      • Nessuna dipendenza diversa Numpy
      • sostituzione chiara dei ndarray (Qualsiasi accettare ndarray classe accetta memmap)
    • Contro:
      • Pezzi di vostro allineamento sono limitati a 2.5G
      • ancora limitata da Numpy rendimento
  • usare Python binding per HDF5, un bigdata -presso formato di file, come PyTables o h5py

    • Pro:
      • formato supporta la compressione, l'indicizzazione, e altre caratteristiche super bella
      • A quanto pare l'ultimo formato di file di petabyte di grandi dimensioni
    • Contro: curva
      • apprendimento di avere un formato gerarchico?
      • necessario definire quali sono le vostre esigenze di performance sono (vedi più avanti) sistema
  • Python's pickling (fuori gara, menzionato per Pythonicity piuttosto che la velocità)

    • Pro:
      • È Pythonic!(Haha)
      • Supporta tutti i tipi di oggetti
    • Contro:
      • Probabilmente lento rispetto ad altri (in quanto volto a qualsiasi non oggetti array)

Numpy.memmap

Dalla documentazione di NumPy.memmap:

creare una memoria-mappa per una matrice memorizzata in un file binario su disco.

file mappati in memoria vengono utilizzati per l'accesso ai piccoli segmenti di file di grandi dimensioni su disco, senza leggere l'intero file in memoria

L'oggetto memmap può essere utilizzato ovunque un ndarray è accettata. Data qualsiasi memmap fp, isinstance(fp, numpy.ndarray) restituisce True.


array HDF5

Dal h5py doc

consente di memorizzare enormi quantità di dati numerici, e facilmente manipolare i dati da NumPy. Ad esempio, è possibile suddividere in serie di dati multi-terabyte archiviati su disco, come se fossero veri array NumPy. Migliaia di set di dati possono essere archiviati in un singolo file, categorizzati e taggati come preferisci.

Il formato supporta la compressione dei dati in vari modi (più bit caricati per lo stesso I/O lettura), ma questo significa che i dati diventa meno facile da interrogare singolarmente, ma nel caso (puramente caricamento matrici/di dumping) potrebbe essere efficiente

+0

ottima risposta. Dovrò definire queste opzioni in relazione a quello che ho ora. – dbliss

+0

hai eseguito la profilazione? come è stata h5py?Sto avendo qualche problema, diventa molto più lento quando ho migliaia di set di dati nello stesso file ... –

12

Ecco un confronto con PyTables.

Non riesco a ottenere fino a (int(1e3), int(1e6) a causa di limitazioni di memoria. Pertanto, ho usato una matrice più piccola:

data = np.random.random((int(1e3), int(1e5))) 

NumPy save:

%timeit np.save('array.npy', data) 
1 loops, best of 3: 4.26 s per loop 

NumPy load:

%timeit data2 = np.load('array.npy') 
1 loops, best of 3: 3.43 s per loop 

PyTables scrittura:

%%timeit 
with tables.open_file('array.tbl', 'w') as h5_file: 
    h5_file.create_array('/', 'data', data) 

1 loops, best of 3: 4.16 s per loop 

PyTables lettura:

%%timeit 
with tables.open_file('array.tbl', 'r') as h5_file: 
     data2 = h5_file.root.data.read() 

1 loops, best of 3: 3.51 s per loop 

I numeri sono molto simili. Quindi nessun guadagno reale con PyTables qui. Ma siamo abbastanza vicini alla massima velocità di scrittura e lettura del mio SSD.

scrittura:

Maximum write speed: 241.6 MB/s 
PyTables write speed: 183.4 MB/s 

lettura:

Maximum read speed: 250.2 
PyTables read speed: 217.4 

compressione non aiuta a causa della casualità dei dati:

%%timeit 
FILTERS = tables.Filters(complib='blosc', complevel=5) 
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file: 
    h5_file.create_carray('/', 'data', obj=data) 
1 loops, best of 3: 4.08 s per loop 

Lettura dei dati compressi diventa un po ' più lento:

%%timeit 
with tables.open_file('array.tbl', 'r') as h5_file: 
    data2 = h5_file.root.data.read() 

1 loops, best of 3: 4.01 s per loop 

Questo è diverso per i dati di regolare:

reg_data = np.ones((int(1e3), int(1e5))) 

scrittura è significativamente più veloce:

%%timeit 
FILTERS = tables.Filters(complib='blosc', complevel=5) 
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file: 
    h5_file.create_carray('/', 'reg_data', obj=reg_data) 

1 loop, meglio del 3: 849 ms per loop

Lo stesso vale per la lettura:

%%timeit 
with tables.open_file('array.tbl', 'r') as h5_file: 
    reg_data2 = h5_file.root.reg_data.read() 

1 loops, best of 3: 1.7 s per loop 

Conclusione: Più i tuoi dati sono regolari più velocemente dovrebbe ottenere usando PyTables.

0

Secondo la mia esperienza, np.save() & np.load() è la soluzione più veloce quando si trasferiscono dati tra il disco rigido e la memoria fino ad ora. Ho fatto molto affidamento sul caricamento dei miei dati su database e sistema HDFS prima di rendermi conto di questa conclusione. I miei test mostrano che: La larghezza di banda dei dati del database (da disco rigido a memoria) potrebbe essere di circa 50 MBps (Byets/Second), ma la larghezza di banda np.load() è quasi uguale alla larghezza di banda massima del mio hard disk: 2GBps (Byets/secondo). Entrambi gli ambienti di test utilizzano la struttura dati più semplice.

E non penso che sia un problema utilizzare diversi secondi per caricare un array con forma: (1e3, 1e6). Per esempio. La forma dell'array è (1000, 1000000), il suo tipo di dati è float128, quindi la dimensione dei dati puri è (128/8) * 1000 * 1.000.000 = 16.000.000.000 = 16 GByli e se ci vogliono 4 secondi, Quindi i dati caricano la larghezza di banda è 16GBytes/4Seconds = 4GBps. La larghezza di banda massima SATA3 è 600MBps = 0,6GBps, i dati che caricano la larghezza di banda sono già 6 volte, le prestazioni di caricamento dei dati potrebbero competere con DDR's maximum bandwidth, che altro vuoi?

Quindi la mia conclusione finale è:

Non utilizzare Pickle di Python, non utilizzare alcun database, non utilizzare alcun sistema di dati grande per memorizzare i dati in hard disk, se è possibile utilizzare np .save() e np.load(). Queste due funzioni sono la soluzione più veloce per trasferire dati tra hard disk e memoria fino ad ora.

Ho anche testato il HDF5, e ha scoperto che è più lento di poltiglia np.load() e np.save(), in modo da utilizzare np.save() & np.load() se hai abbastanza Memoria DDR nella tua platfrom.

+1

Se non riesci a raggiungere la larghezza massima del tuo dispositivo di archiviazione usando HDF5 hai fatto qualcosa di sbagliato. E ci sono molte cose che possono andare storte. (chunk-cache, chunkshape, indicizzazione di fantasia, ...) – max9111

+0

max9111, no, non sono d'accordo. Alcune restrizioni derivano dal framework del software, solo la messa a punto del sistema non può imporre le prestazioni. Dopo aver trovato l'enorme differenza di prestazioni tra np.load() e mysql sul caricamento dei dati, credo che la struttura del software sia molto importante, una scelta corretta potrebbe aiutarci a risparmiare un sacco di tempo. –

+1

Prova ad esempio questo https://stackoverflow.com/a/48997927/4045774 con e senza compressione (i limiti di compressione a circa 500-800 MB/s. Per dati ben comprimibili puoi ottenere molto più throuput con HDF 5 su un HDD o persino un SSD SATA3, ma il vantaggio principale è la lettura o la scrittura di parti di array lungo l'asse dell'abitacolo alla velocità IO sequenziale. Se la velocità IO è importante, è anche probabile che l'array sia più grande della RAM ... – max9111

Problemi correlati