2015-08-23 5 views
18

Sto cercando di calcolare la somiglianza del coseno di 100.000 vettori e ciascuno di questi vettori ha 200.000 dimensioni.Come posso leggere e scrivere in modo efficiente file troppo grandi per adattarli alla memoria?

Dalla lettura di altre domande, so che memmap, PyTables e h5py sono le mie migliori scommesse per la gestione di questo tipo di dati e attualmente sto lavorando con due memmap; uno per leggere i vettori, l'altro per memorizzare la matrice delle somiglianze del coseno.

Ecco il mio codice:

import numpy as np 
import scipy.spatial.distance as dist 

xdim = 200000 
ydim = 100000 

wmat = np.memmap('inputfile', dtype = 'd', mode = 'r', shape = (xdim,ydim)) 
dmat = np.memmap('outputfile', dtype = 'd', mode = 'readwrite', shape = (ydim,ydim)) 

for i in np.arange(ydim)): 
    for j in np.arange(i+1,ydim): 
     dmat[i,j] = dist.cosine(wmat[:,i],wmat[:,j]) 
     dmat.flush() 

Attualmente, htop riferisce che sto usando 224g della memoria VIRT, e 91.2G delle fonti rinnovabili di memoria che è in continua crescita. Mi sembra che, alla fine del processo, l'intera matrice di output verrà memorizzata, cosa che sto cercando di evitare.

DOMANDA: È questo un uso corretto di memmaps, sto scrivendo per il file di output in modo efficiente la memoria (e con questo intendo che solo le parti necessarie dei file di ingresso e uscita cioè dmat[i,j] e wmat[:,i/j], sono memorizzato in memoria)?

Se no, cosa ho fatto di sbagliato, e come posso risolvere questo problema?

Grazie per qualsiasi consiglio che potete avere!

EDIT: Ho appena capito che htop sta segnalando l'utilizzo totale della memoria di sistema a 12G, così sembra si sta lavorando dopo tutto ... qualcuno là fuori che mi può illuminare? RES è ora a 111G ...

EDIT2: la memmap viene creata da una matrice 1D costituita da molti e molti decimali lunghi abbastanza vicini a 0, che è modellata sulle dimensioni desiderate. La memmap è quindi simile a questa.

memmap([[ 9.83721223e-03, 4.42584107e-02, 9.85033578e-03, ..., 
    -2.30691545e-07, -1.65070799e-07, 5.99395837e-08], 
    [ 2.96711345e-04, -3.84307391e-04, 4.92968462e-07, ..., 
    -3.41317722e-08, 1.27959347e-09, 4.46846438e-08], 
    [ 1.64766260e-03, -1.47337747e-05, 7.43660202e-07, ..., 
     7.50395136e-08, -2.51943163e-09, 1.25393555e-07], 
    ..., 
    [ -1.88709000e-04, -4.29454722e-06, 2.39720287e-08, ..., 
    -1.53058717e-08, 4.48678211e-03, 2.48127260e-07], 
    [ -3.34207882e-04, -4.60275148e-05, 3.36992876e-07, ..., 
    -2.30274532e-07, 2.51437794e-09, 1.25837564e-01], 
    [ 9.24923862e-04, -1.59552854e-03, 2.68354822e-07, ..., 
    -1.08862665e-05, 1.71283316e-07, 5.66851420e-01]]) 
+0

Non direi che questa domanda è "errata" per SO, ma probabilmente otterrai una risposta migliore su http://codereview.stackexchange.com poiché questo riguarda più l'architettura, quindi un bug reale o un how-to. – Victory

+4

@Victory CR è più su * codice * che architettura. Non dire che è "sbagliato" per CR, ma penso che OP probabilmente otterrà una risposta migliore in SO. :) –

+0

beh, in sostanza sto chiedendo un how-to leggere/scrivere file di grandi dimensioni da/su disco in modo efficiente. Sono confuso perché sto ottenendo informazioni contrastanti da htop = S – Jojanzing

risposta

7

In termini di utilizzo della memoria, non c'è niente di particolarmente sbagliato in quello che stai facendo in questo momento. Gli array Memmapped sono gestiti a livello del sistema operativo: i dati da scrivere vengono generalmente conservati in un buffer temporaneo e vengono memorizzati sul disco solo quando il sistema operativo lo ritiene necessario. Il tuo sistema operativo non dovrebbe mai consentire di esaurire la memoria fisica prima di svuotare il buffer di scrittura.

Vorrei sconsigliare di chiamare flush su ogni iterazione poiché ciò vanifica lo scopo di consentire al sistema operativo di decidere quando scrivere sul disco al fine di massimizzare l'efficienza. Al momento stai scrivendo solo valori float alla volta.


In termini di efficienza IO e CPU, il funzionamento su una singola linea per volta è quasi certamente non ottimale. Le letture e le scritture sono in genere più veloci per blocchi di dati contigui e di grandi dimensioni, e allo stesso modo il calcolo sarà probabilmente molto più veloce se è possibile elaborare più righe contemporaneamente utilizzando la vettorizzazione. La regola generale è quella di elaborare un grosso blocco dell'array che si adatta alla memoria (inclusi gli array intermedi creati durante il calcolo).

Here's an example mostrando quanto è possibile velocizzare le operazioni su array memmapped elaborandoli in blocchi di dimensioni appropriate.

Un'altra cosa che può fare una grande differenza è il layout di memoria degli array di input e output. Per impostazione predefinita, np.memmap fornisce una matrice C-contigua (riga-principale). L'accesso a wmat per colonna sarà quindi molto inefficiente, dal momento che stai indirizzando posizioni non adiacenti sul disco. Sarebbe molto meglio se wmat fosse F-contiguo (colonna-principale) su disco, o se tu stessi accedendo per riga.

Lo stesso consiglio generale si applica all'utilizzo di HDF5 invece di memmap, anche se tenete presente che con HDF5 dovrete gestire da soli tutta la gestione della memoria.

+0

Per ottenere un array F-contiguo, principale della colonna, sarebbe sufficiente creare la memmap con 'order = 'F''? Grazie per la descrizione dettagliata. Anche il codice nel link sembra fantastico, farò un tentativo. – Jojanzing

+1

Questo non sarebbe d'aiuto nel tuo esempio, dal momento che 'wmat' è una matrice preesistente sul disco che stai aprendo in modalità di sola lettura. Dovresti scrivere effettivamente 'wmat' su disco nel formato della colonna principale per cominciare. –

+0

ah capisco ... lo terrò a mente in futuro. Un'ultima domanda, ci sono motivi validi per usare HDF5 su memmaps? – Jojanzing

7

Le mappe di memoria sono esattamente ciò che dice il nome: mappature dei settori del disco (virtuali) in pagine di memoria. La memoria è gestita dal sistema operativo su richiesta. Se c'è abbastanza memoria, il sistema conserva parti dei file in memoria, forse riempendo l'intera memoria, se non ne rimane abbastanza, il sistema può scartare le pagine lette dal file o può scambiarle nello spazio di swap. Normalmente è possibile fare affidamento sul sistema operativo il più efficiente possibile.

+0

Vedo, sta usando quanta più memoria possibile, nel modo più efficiente possibile. Grazie per averlo chiarito! Hai idea del perché l'utilizzo del sistema sia solo di 12G anche se VIRT è a 224G e RES ora stabile a 149G? – Jojanzing