2013-04-12 6 views
8

Ho alcuni file di grandi dimensioni (anche gzip circa 10 GB), che contengono un'intestazione ASCII e quindi in linea di principio numpy.recarrays di circa 3 MB ciascuno, li chiamiamo "eventi". Il mio primo approccio si presentava così:numpy.fromfile efficiente su file zippati?

f = gzip.GzipFile(filename) 
f.read(10000) # fixed length ascii header 
event_dtype = np.dtype([ 
     ('Id', '>u4'),    # simplified 
     ('UnixTimeUTC', '>u4', 2), 
     ('Data', '>i2', (1600,1024)) 
     ]) 
event = np.fromfile(f, dtype = event_dtype, count=1) 

Tuttavia, questo non è possibile, dal momento che np.fromfile ha bisogno di un oggetto file vero e proprio, perché rende davvero chiamate di basso livello (trovato un biglietto piuttosto vecchio https://github.com/numpy/numpy/issues/1103).

Quindi, come ho capito che devo fare in questo modo:

s = f.read(event_dtype.itemsize) 
event = np.fromstring(s, dtype=event_dtype, count=1) 

E sì, funziona! Ma non è terribilmente inefficiente? Non sono assegnati i mem e vengono raccolti i rifiuti per ogni evento? Sul mio laptop raggiungo qualcosa come 16 eventi/s, ad esempio ~ 50MB/s

Mi chiedo se qualcuno conosce un modo intelligente, di assegnare il mem una volta e poi lasciare che numpy legga direttamente in quel mem.

Btw. Sono un fisico, quindi ... beh, sono ancora un principiante in questo business.

+1

Il tempo impiegato dall'I/O è simile a * migliaia * di volte più grande del tempo impiegato per allocare/deallocare quella stringa. Dovresti profilare il codice per vedere dove si trova il collo di bottiglia e poi ottimizzarlo ... indovinare dove si trova il collo di bottiglia è brutto, anche di più se non sei abituato a programmare in modo efficiente. – Bakuriu

+1

Finché stai bene con gli array di sola lettura, potresti usare 'numpy.frombuffer' per evitare di duplicare la memoria e usare semplicemente la stringa come buffer di memoria. –

+0

@Bakariu, grazie per il fraseggio chiaro che. Non ho esperienza con il codice di profilazione. E 'bello saperlo, quella supposizione è cattiva. –

risposta

3

@Bakuriu è probabilmente corretto che questo è probabilmente un micro-ottimizzazione. Il tuo collo di bottiglia è quasi sicuramente IO e, successivamente, decompressione. L'allocazione della memoria due volte probabilmente non è significativa.

Tuttavia, se si desidera evitare l'allocazione di memoria aggiuntiva, è possibile utilizzare numpy.frombuffer per visualizzare la stringa come matrice numpy.

Ciò evita la duplicazione della memoria (la stringa e l'array utilizzano lo stesso buffer di memoria), ma l'array sarà di sola lettura, per impostazione predefinita. Puoi quindi modificarlo per consentire la scrittura, se necessario.

Nel tuo caso, sarebbe semplice come la sostituzione fromstring con frombuffer:

f = gzip.GzipFile(filename) 
f.read(10000) # fixed length ascii header 
event_dtype = np.dtype([ 
     ('Id', '>u4'),    # simplified 
     ('UnixTimeUTC', '>u4', 2), 
     ('Data', '>i2', (1600,1024)) 
     ]) 
s = f.read(event_dtype.itemsize) 
event = np.frombuffer(s, dtype=event_dtype, count=1) 

solo per dimostrare che la memoria non sia duplicato utilizzando questo approccio:

import numpy as np 

x = "hello" 
y = np.frombuffer(x, dtype=np.uint8) 

# Make "y" writeable... 
y.flags.writeable = True 

# Prove that we're using the same memory 
y[0] = 121 
print x # <-- Notice that we're outputting changing y and printing x... 

Questo produce: yello invece di hello.

Indipendentemente dal fatto che si tratti di un'ottimizzazione significativa in questo caso particolare, è un approccio utile da tenere presente.