Come 'copiare' una matrice senza creare una matrice temporanea in memoria che ha causato un overflow della memoria?


9

Assegnando una matrice a una memoria allocata molto più grande, matlab in qualche modo la duplicherà mentre la 'copia', e se la matrice da copiare è abbastanza grande, ci sarà un overflow di memoria. Questo è il codice di esempio:

main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
    parfor i=1:n
        slice_matrix(:,:,i)=gather(gpuArray(rand(500,500)));
    end
    main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end

Qualche modo di 'fracassare' il slice_matrixsul main_matsenza il sovraccarico? Grazie in anticipo.

MODIFICARE:

L'overflow si main_matè verificato quando è stato allocato in anticipo. Se main_matviene inizializzato con main_mat=zeros(500,500,1);(dimensioni più piccole), l'overflow non si verificherà, ma rallenterà poiché l'allocazione non viene eseguita prima dell'assegnazione della matrice. Ciò ridurrà significativamente le prestazioni man mano che kaumenta la gamma .


1
Per quanto riguarda i tuoi loop: si consiglia di impostare il loop esterno su un parforloop per scopi di ottimizzazione . Inoltre, parforcopia i tuoi dati su ciascun lavoratore separato, assumendo così 4 lavoratori che duplicano i tuoi dati quattro volte nella RAM.
Adriaan,

1
Qual è la tua indicazione che Matlab sta effettivamente duplicando la memoria? Stai usando la memoryfunzione? Il responsabile delle attività? Un errore di memoria da Matlab? A quale linea di codice sta accadendo?
Eliahu Aaron,

Come puoi vedere dove ho commentato il codice, main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)è dove si verifica il problema di overflow della memoria. È verificato quando ho allocato in main_matanticipo, traboccerà, se non lo faccio, non lo farà. Matlab restituirà "errore di memoria esaurita".
Gregor Isack,

La tua matrice 500x500x2000 si adatta alla memoria? È ~ 4 Gb. Consultare stackoverflow.com/q/51987892/7328782 per i motivi per cui l'errore di memoria insufficiente potrebbe verificarsi solo durante la scrittura sull'array.
Cris Luengo,

Per capire meglio il tuo problema, potresti inserire un h=h+slice_matrix(end)precedente main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix;(e inizializzare h con 0)? Sospetto che questa nuova riga aggiunta causi già problemi di memoria.
Daniel,

Risposte:


4

Il problema principale è che i numeri occupano più spazio degli zeri. main_mat=zeros(500,500,2000);richiede poca RAM mentre main_mat = rand(500,500,2000);richiede molto, non importa se usi GPU o parfor (in effetti, parfor ti farà usare più RAM). Quindi questo non è un gonfiore innaturale della memoria. Seguendo il seguente link di Daniel, sembra che l'assegnazione degli zeri crei solo puntatori alla memoria e che la memoria fisica venga riempita solo quando si utilizza la matrice per "numeri". Questo è gestito dal sistema operativo. E è previsto per Windows, Mac e Linux, o lo fai con Matlab o altre lingue come C.


In questo momento non capisco più MATLAB. Una volta che digito i comandi con l' zerosintera memoria virtuale viene effettivamente allocata, ma non viene utilizzata memoria. whosmostra le stesse dimensioni per entrambe le matrici, mentre il mio sistema operativo mostra un diverso consumo di memoria. Ho cancellato il mio commento perché la tua risposta non è assolutamente sbagliata.
Daniel

3
Ho trovato qualcosa che spiega questo: stackoverflow.com/questions/51987892/…
Daniel

Bella risposta! Grazie.
JLev

@Gregor: credo di confermare questo, provalo con onesinvece di zeros, questo assicura che la memoria sia effettivamente allocata al momento della chiamata della rispettiva funzione.
Daniel

Quando capisco tutto bene, la conclusione è: non esiste una copia temporanea. Le eccezioni di memoria insufficiente sorgono perché main_matvengono assegnati valori diversi da zero. In precedenza era stata assegnata solo la memoria virtuale (spazio degli indirizzi), ora è assegnata alla memoria fisica.
Daniel,

1

La rimozione parforprobabilmente risolverà il tuo problema.

parfornon è utile lì. MATLAB parfornon utilizza il parallelismo della memoria condivisa (ovvero non avvia nuovi thread) ma piuttosto il parallelismo della memoria distribuita (avvia nuovi processi). È progettato per distribuire il lavoro su un set o su nodi di lavoro. E sebbene funzioni anche all'interno di un nodo (o di un singolo computer desktop) per distribuire il lavoro su più core, non è un modo ottimale di parallelismo all'interno di un nodo.

Ciò significa che ciascuno dei processi avviati parfordeve avere una propria copia slice_matrix, che è la causa della grande quantità di memoria utilizzata dal programma.

Vedi "Decidi quando usare parfor" nella documentazione MATLAB per saperne di più su parfore quando usarlo.


1
La rimozione parfor è l'unico modo ? L'elaborazione funziona meglio quando l'ho progettata in quel modo, poiché tutto all'interno parforè intenso per CPU e GPU, quindi ha migliorato significativamente le prestazioni.
Gregor Isack,

@GregorIsack: sono andato con il tuo codice di esempio, non sapevo che in realtà hai lavorato molto all'interno di parfor. Se è così, allora sì, è probabilmente utile. - Forse se slice_matrixnon gpuarraylo è, non verrà copiato nell'assegnazione.
Cris Luengo,

Hmmm anche se slice_matrixnon è un gpuArray, sto ancora ricevendo il sintomo di tracimazione. Lascerò aperta questa domanda, vediamo se ci sono soluzioni alternative. Grazie per la risposta però!
Gregor Isack,

0

Presumo che il tuo codice sia solo un codice di esempio e che rand()rappresenti un'abitudine nel tuo MVE. Quindi ci sono alcuni suggerimenti e trucchi per l'utilizzo della memoria in matlab.

C'è un frammento dei manuali di formazione di The MathWorks:

Quando si assegna una variabile a un'altra in MATLAB, come accade quando si passano parametri in una funzione, MATLAB crea in modo trasparente un riferimento a quella variabile. MATLAB interrompe il riferimento e crea una copia di quella variabile, solo quando il codice modifica uno o più dei valori. Questo comportamento, noto come copia su scrittura o copia lazy lenta, difende il costo della copia di grandi set di dati fino a quando il codice non modifica un valore. Pertanto, se il codice non esegue modifiche, non è necessario ulteriore spazio di memoria e tempo di esecuzione per copiare le variabili.

La prima cosa da fare sarebbe controllare l'efficienza (memoria) del tuo codice. Anche il codice di programmatori eccellenti può essere ulteriormente ottimizzato con (un po ') potere cerebrale. Ecco alcuni suggerimenti sull'efficienza della memoria

  • fare uso della vettorializzazione nativa di matlab, ad es sum(X,2) . mean(X,2),std(X,[],2)
  • assicurarsi che matlab non debba espandere le matrici ( espansione implicita stata modificata di recente). Potrebbe essere più efficiente utilizzarebsxfun
  • utilizzare operazioni sul posto, ad es x = 2*x+3 anzichéx = 2*x+3
  • ...

Tenere presente che l'ottimizzazione dell'utilizzo della memoria non è la stessa di se si desidera ridurre i tempi di calcolo. Pertanto, potresti voler considerare di ridurre il numero di lavoratori o astenersi dall'utilizzare il parfor-loop. (Poiché parfornon è possibile utilizzare la memoria condivisa, non esiste copia su scrittura con l'utilizzo di Parallel Toolbox.

Se vuoi dare un'occhiata più da vicino alla tua memoria , a ciò che è disponibile e che può essere utilizzato da Matlab, dai un'occhiata feature('memstats'). Ciò che è interessante per te è la memoria virtuale che è

Memoria totale e disponibile associata all'intero processo MATLAB. È limitato dall'architettura del processore e dal sistema operativo. o usa questo comando [user,sys] = memory.

Nodo lato rapido : Matlab memorizza le matrici in modo coerente nella memoria. È necessario disporre di un grande blocco di RAM libera per matrici di grandi dimensioni. Questo è anche il motivo per cui vuoi allocare variabili, perché cambiarle dinamicamente costringe Matlab a copiare l'intera matrice in un punto più grande della RAM ogni volta che supera il punto corrente.

Se hai davvero problemi di memoria , potresti semplicemente voler approfondire l'arte dei tipi di dati, come richiesto nelle lingue di livello inferiore. Ad esempio, è possibile dimezzare l'utilizzo della memoria utilizzando la precisione singola direttamente dall'inizio main_mat=zeros(500,500,2000,'single');; a proposito, funziona anche con rand(...,'single')e più funzioni native, anche se alcune delle più sofisticate funzioni matlab richiedono input di tipo double, che è possibile ripubblicato di nuovo.


0

Se capisco correttamente il problema principale è che parfornon consente di condividere la memoria. Pensa a ogni lavoratore parfor come a un'istanza di matlab separata.

Esiste fondamentalmente solo una soluzione per questo che conosco (che non ho mai provato), ovvero la matrice condivisa su Fileexchange: https://ch.mathworks.com/matlabcentral/fileexchange/28572-sharedmatrix

Altre soluzioni: come altri hanno suggerito: rimuovere parfor è sicuramente una soluzione, ottenere più ram, usare array alti (che usano hard disk quando ram è pieno, leggi qui ), dividere le operazioni in blocchi più piccoli, ultimo ma non meno importante, considerare un'alternativa diversa da Matlab.


0

È possibile utilizzare il seguente codice. In realtà non hai bisogno di slice_matrix

main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
   parfor i=1:n
       main_mat(:,:,1+(k-1)*n + i - 1) = gather(gpuArray(rand(500,500)));
   end
   %% now you don't need this main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end

Non puoi farlo all'interno di un ciclo parfor
Gregor Isack,

Ci hai provato?
Mayank1513

C'è un motivo per cui l'ho spostato fuori dal circuito di parfoor. Non ho provato lo stesso identico codice, ma sapevo che non avrebbe funzionato a causa dell'indicizzazione.
Gregor Isack,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.