2014-10-01 19 views
5

Ho bisogno di scrivere un array che è troppo grande per adattarsi alla memoria di un file binario .mat. Ciò può essere eseguito con il comando matfile, che consente l'accesso casuale a un file .mat sul disco.Preallocazione di un array di grandi dimensioni in un MATLAB matfile con qualcosa diverso da zero

Sto cercando di preallocare la matrice in questo file, e l'approccio consigliato da un MathWorks blog è

matObj = matfile('myBigData.mat','Writable',true); 
matObj.X(10000,10000) = 0; 

Questo funziona, ma mi lascia con una vasta gamma di zeri - che è rischioso, alcuni dei i valori genuini con cui lo popolerò potrebbero anche essere zero. Per gli array più piccoli, vorrei fare tipicamente

smallarray = nan(20,20); 

Ma se provo questo approccio per la vasta gamma ottengo un errore "out of memory"; presumibilmente la funzione nan() sta producendo la grande serie di NaN s in memoria.

Come posso preassegnare un array di grandi dimensioni con qualcosa di diverso da zero?

+1

Hmm, domanda correlata Suppongo sia se ci sia * bisogno * di preallocare in questo caso. Il solito vantaggio prestazionale sarà presumibilmente insignificante rispetto al tempo impiegato per scrivere cose sul disco ... Immagino che eviti che il file sia frammentato? – Flyto

risposta

3

Ho trovato che né le risposte di sclarke81 né di Sam Robert funzionano effettivamente, e dubito che il concetto di preallocazione si applichi a matfile. I risultati riportati di seguito sono stati ottenuti su una CPU i7-3770 a 3,4 GHz con 16,8 GB di memoria principale, eseguendo Matlab R2013a su Linux 3.16.

Il codice

mf = matfile(fn, 'Writable', true); 
mf.x(5000, 200000) = 0; 
clear mf 

teoricamente "alloca" 8 GB di memoria su disco, inizializzato a 0. Tuttavia, il file risultante ha una dimensione di 4726 byte e il processo richiede meno di 0,01 secondi. Posso aumentare la dimensione 10 o 100 volte e non cambia molto. Strano. Btw., Lo clear alla fine è lì per assicurare che il file sia scritto e chiuso da Matlab.

Spesso vogliamo preallocare inizializzazione a NaN invece di 0. In questo modo il ricevuto modo

mf = matfile(fn, 'Writable', true); 
mf.x = nan(5000, 200000); 
clear mf 

prende 11 secondi ed i risultati in un file di 57 MB. Ma come ha sottolineato il PO, questo approccio non ha senso perché prima genera l'intera matrice di 8 GB in memoria e poi la scrive, che sconfigge lo scopo di matfile. Se la matrice si adatta alla memoria, in primo luogo non c'è motivo di conservare i dati in un file durante l'elaborazione.

Sam Roberts propone di primo allocare/inizializzare a 0 come sopra, e quindi modificare i valori NaN:

mf = matfile(fn, 'Writable', true); 
mf.x(5000, 200000) = 0; 
mf.x = mf.x * nan; 
clear mf 

Questo richiede 16 secondi, con la stessa dimensione del file risultante. Tuttavia, questo non è in alcun modo migliore dell'approccio naive di cui sopra, perché sulla terza riga l'intera matrice viene letta in memoria, moltiplicata con NaN scalare in memoria, e quindi scritta di nuovo, portando ad un consumo di memoria di picco di 8 GB. (Questo non è solo coerente con la semantica matfile -variabili spiegato nel documentation, ma anche controllato con un monitor di utilizzo della memoria.)

sclarke81 propone di evitare invece generazione della matrice di memoria in questo modo:

mf = matfile(fn, 'Writable', true); 
mf.x(1 : 5000, 1 : 200000) = nan; 
clear mf 

l'idea è probabilmente che solo un NaN scalare viene generato in memoria e quindi copiato in ogni elemento della matrice su disco. Tuttavia, non è quello che succede. In realtà, questo metodo sembra consumare circa 8,38 GB di memoria al picco, il 12% in più rispetto all'approccio ingenuo!

Ora più nel merito della preallocazione con matfile. Se non si preallocare, ma riempie l'array row-wise con NaNs

mf = matfile(fn, 'Writable', true); 
for i = 1 : 5000 
    mf.x(i, 1 : 200000) = nan(1, 200000); 
end 
clear mf 

questo richiede 27 secondi. Ma, se uno prealloca inizializzazione a 0 e poi fila-wise sovrascrittura di NaN

mf = matfile(fn, 'Writable', true); 
mf.x(5000, 200000) = 0; 
for i = 1 : 5000 
    mf.x(i, 1 : 200000) = nan(1, 200000); 
end 
clear mf 

ci vogliono secoli: il processo è stato solo circa il 3% finito quando ho abortito dopo 45 minuti, estrapolando a circa un giorno di runtime totale!

Il comportamento di matlab.io.MatFile è scuro e misterioso, e sembra che al momento, solo il test approfondito porterà a un modo efficace per utilizzare questa funzione. Tuttavia, si può concludere che la preallocazione è una cattiva idea quando si tratta di matfile.

+0

@A Donda: apprezzare i risultati quantitativi per gli approcci identificati. I limiti MATLAB/RAM/OS sono noti, ma l'HDD.IO è il killer. Trascorrere giorni in preallocazione di 'NaN's non è altro che un lussuoso spreco di risorse. Come presentato in >>> http://stackoverflow.com/a/27083554/3666197 i problemi 'matfile'/HDF5 e i veri BigDATA richiedono strategie di manipolazione dei dati più accurate di una * preallocazione *. [N.B.Il formato del formato HDF5 è nel supporto per l'implementazione efficiente di modifiche dinamiche degli elementi di dati nelle bilance BigDATA, non per il contenuto * statico * – user3666197

+0

Molto interessante! Riguardo il tuo ultimo ritrovamento ... Mi chiedo se la conversione da 1 (un int) a nan (un float) potrebbe essere parzialmente responsabile del rallentamento. Al momento non ho un'installazione MATLAB, quindi non posso controllarlo da solo. Potresti ripetere l'ultimo esperimento ma inizializzare 'mf.x' su nan (o un float come 0.1)? – GnomeDePlume

+0

... Sto solo cercando di verificare che NaN sia un float in MATLAB. Mi scuso se questo non è corretto. Diventando troppo abituato a Python/Numpy! – GnomeDePlume

0

Questo metodo funziona per me. Si noti che è necessario specificare l'intervallo per l'indice di matrice (X(1:10000,1:10000)), altrimenti è sufficiente impostare il singolo elemento su 10000,10000 su NaN.

matObj = matfile('myBigData.mat','Writable',true); 
matObj.X(1:10000,1:10000) = NaN; 
0

Puoi fare qualcosa di simile:

matObj = matfile('myBigData.mat','Writable',true); 
matObj.X(10000,10000) = 0; 

e poi

matObj.X = matObj.X + 1; 

o

matObj.X = matObj.X * NaN; 

?

+0

Questa è una buona idea, e funziona. Accetterò l'altra risposta perché comporta meno passaggi, ma grazie. – Flyto

+0

Sono d'accordo - l'altra risposta è migliore. –

0

Ciò può essere eseguito mediante il mapping della memoria di un file binario, utilizzando MappedTensor (auto-citazione).

% - Create and map a large 'double' tensor to a temporary file on disk 
mt = MappedTensor(100, 100, 100); 

% - % Write 'nan' to every element of 'mt', without allocating entire tensor 
mt(:) = nan; 

Si può tentare un approccio simile con memmapfile, ma memmapfile alloca spazio per l'intero tensore quando si scrive in un file mappato.

È possibile preassegnare e quindi mappare uno specifico file binario utilizzando fsutil su un PC o fallocate su un computer Mac o Linux.

Problemi correlati