5

Sono nuovo ad usare gli array distribuiti e codistribuiti in MATLAB. Il codice parallelo che ho prodotto funziona, ma è molto più lento della versione seriale e non ho idea del perché. Gli esempi di codice sotto computano gli autovalori delle matrici hessiane dai dati volumetici.Matlab elaborazione parallela lenta con array distribuiti

versione di serie:

S = size(D); 
Dsmt=imgaussian(D,2,20); 
[fx, fy, fz] = gradient(Dsmt); 
DHess = zeros([3 3 S(1) S(2) S(3)]); 
[DHess(1,1,:,:,:), DHess(1,2,:,:,:), DHess(1,3,:,:,:)] = gradient(fx); 
[DHess(2,1,:,:,:), DHess(2,2,:,:,:), DHess(2,3,:,:,:)] = gradient(fy); 
[DHess(3,1,:,:,:), DHess(3,2,:,:,:), DHess(3,3,:,:,:)] = gradient(fz); 

d = zeros([3 S(1) S(2) S(3)]); 
for i = 1 : S(1) 
    fprintf('Slice %d out of %d\n', i, S(1)); 
    for ii = 1 : S(2) 
     for iii = 1 : S(3) 
      d(:,i,ii,iii) = eig(squeeze(DHess(:,:,i,ii,iii))); 
     end 
    end 
end 

versione parallela:

S = size(D); 
Dsmt=imgaussian(D,2,20); 
[fx, fy, fz] = gradient(Dsmt); 
DHess = zeros([3 3 S(1) S(2) S(3)]); 
[DHess(1,1,:,:,:), DHess(1,2,:,:,:), DHess(1,3,:,:,:)] = gradient(fx); 
[DHess(2,1,:,:,:), DHess(2,2,:,:,:), DHess(2,3,:,:,:)] = gradient(fy); 
[DHess(3,1,:,:,:), DHess(3,2,:,:,:), DHess(3,3,:,:,:)] = gradient(fz); 
CDHess = distributed(DHess); 
spmd 
    d = zeros([3 S(1) S(2) S(3)], codistributor('1d',4)); 
    for i = 1 : S(1) 
     fprintf('Slice %d out of %d\n', i, S(1)); 
     for ii = 1 : S(2) 
      for iii = drange(1 : S(3)) 
       d(:,i,ii,iii) = eig(squeeze(CDHess(:,:,i,ii,iii))); 
      end 
     end 
    end 
end 

Se qualcuno potesse far luce sulla questione sarei molto grato

+0

Quanto dura una singola iterazione? – Jonas

+0

stai aprendo il tuo matlabpool? – Rasman

+0

@Jonas Una singola iterazione (sulla variabile i) sulla versione seriale richiede circa 1,7 secondi.Una singola iterazione sulla versione parallela non viene completata in oltre 5 minuti, momento in cui ho terminato l'esecuzione. – Hampycalc

risposta

2

Ecco una versione riscritta del codice. Ho diviso il lavoro sul ciclo più esterno, non come nel tuo caso: il ciclo più interno. Ho anche assegnato esplicitamente parti locali del vettore dei risultati d e della parte locale della matrice di Hessian.

Nel codice si fa affidamento su drange per suddividere il lavoro e si accede direttamente agli array distribuiti per evitare di estrarre la parte locale. Certo, non dovrebbe comportare un così grande rallentamento se MATLAB ha fatto tutto correttamente. La linea di fondo è, non so perché il tuo codice è così lento - molto probabilmente perché MATLAB esegue alcuni accessi ai dati remoti nonostante tu abbia distribuito le tue matrici.

In ogni caso, il codice riportato di seguito viene eseguito e offre una buona accelerazione sul mio computer utilizzando 4 laboratori. Ho generato dati di input casuali sintetici per avere qualcosa su cui lavorare. Dai un'occhiata ai commenti. Se qualcosa non è chiaro, posso elaborare in seguito.

clear all; 

D = rand(512, 512, 3); 
S = size(D); 
[fx, fy, fz] = gradient(D); 

% this part could also be parallelized - at least a bit. 
tic; 
DHess = zeros([3 3 S(1) S(2) S(3)]); 
[DHess(1,1,:,:,:), DHess(1,2,:,:,:), DHess(1,3,:,:,:)] = gradient(fx); 
[DHess(2,1,:,:,:), DHess(2,2,:,:,:), DHess(2,3,:,:,:)] = gradient(fy); 
[DHess(3,1,:,:,:), DHess(3,2,:,:,:), DHess(3,3,:,:,:)] = gradient(fz); 
toc 

% your sequential implementation 
d = zeros([3, S(1) S(2) S(3)]); 
disp('sequential') 
tic 
for i = 1 : S(1) 
    for ii = 1 : S(2) 
     for iii = 1 : S(3) 
      d(:,i,ii,iii) = eig(squeeze(DHess(:,:,i,ii,iii))); 
     end 
    end 
end 
toc 

% my parallel implementation 
disp('parallel') 
tic 
spmd 
    % just for information 
    disp(['lab ' num2str(labindex)]); 

    % distribute the input data along the third dimension 
    % This is the dimension of the outer-most loop, hence this is where we 
    % want to parallelize! 
    DHess_dist = codistributed(DHess, codistributor1d(3)); 
    DHess_local = getLocalPart(DHess_dist); 

    % create an output data distribution - 
    % note that this time we split along the second dimension 
    codist = codistributor1d(2, codistributor1d.unsetPartition, [3, S(1) S(2) S(3)]); 
    localSize = [3 codist.Partition(labindex) S(2) S(3)]; 

    % allocate local part of the output array d 
    d_local = zeros(localSize); 

    % your ordinary loop, BUT! the outermost loop is split amongst the 
    % threads explicitly, using local indexing. In the loop only local parts 
    % of matrix d and DHess are accessed 
    for i = 1:size(d_local,2) 
     for ii = 1 : S(2) 
      for iii = 1 : S(3) 
       d_local(:,i,ii,iii) = eig(squeeze(DHess_local(:,:,i,ii,iii))); 
      end 
     end 
    end 

    % assemble local results to a codistributed matrix 
    d_dist = codistributed.build(d_local, codist); 
end 
toc 

isequal(d, d_dist) 

E l'uscita

Elapsed time is 0.364255 seconds. 
sequential 
Elapsed time is 33.498985 seconds. 
parallel 
Lab 1: 
    lab 1 
Lab 2: 
    lab 2 
Lab 3: 
    lab 3 
Lab 4: 
    lab 4 
Elapsed time is 9.445856 seconds. 

ans = 

    1 

Edit Ho controllato le prestazioni su una matrice rimodellata DHess=[3x3xN]. La performance non è molto migliore (10%), quindi non è sostanziale. Ma forse è possibile implementare lo eig in modo un po 'diverso? Dopotutto, quelle sono le matrici 3x3 con le quali hai a che fare.

+0

È fantastico che tu abbia avuto il tempo di fornire questo esempio in quanto potrò utilizzare queste idee con molti progetti futuri. Un paio di domande: ho usato 'drange' nel mio codice, qual è lo scopo di questo se devi usare 'getLocalPart'. Usando il tuo codice sto ricevendo un errore: 'Errore nell'uso di distcompserialize Errore durante la serializzazione' sulla linea DHess_dist = codistributed (DHess, codistributor1d (3)); La dimensione del mio input D è di circa 512x512x200, forse la dimensione è un problema; anche se sembra che non dovrebbe importare quanto sia grande la matrice, poiché questo è uno degli scopi principali dell'elaborazione parallela – Hampycalc

+0

Devo anche dichiarare che D è un doppio. – Hampycalc

+0

@Hampycalc Mi dispiace, ho ovviamente perso il fatto che hai usato 'drange'. Modificherò la mia risposta: dopo tutto hai diviso il tuo lavoro. Colpa mia. – angainor

1

Lei non ha specificato dove si' hai aperto il tuo matlabpool, e questo sarà il fattore principale che determina quale velocità ottieni.

Se si utilizza lo scheduler "locale", spesso non vi è alcun vantaggio nell'utilizzo di array distribuiti. In particolare, se le operazioni che richiedono tempo sono multithread in MATLAB già, allora quasi certamente rallenteranno quando si utilizza lo scheduler locale poiché i lavoratori matlabpool funzionano in modalità single-threaded.

Se si sta utilizzando un altro programma di pianificazione con gli operatori su una macchina separata, è possibile che si ottenga la velocità, ma ciò dipende da ciò che si sta facendo. C'è un esempio qui http://www.mathworks.com/products/parallel-computing/examples.html?file=/products/demos/shipping/distcomp/paralleldemo_backslash_bench.html che mostra alcuni parametri di riferimento dell'operatore \ di MATLAB.

Infine, vale la pena notare che l'indicizzazione di array distribuiti è purtroppo piuttosto lenta, soprattutto rispetto all'indicizzazione incorporata di MATLAB. Se è possibile estrarre la 'parte locale' degli array codistribuiti all'interno del blocco spmd e lavorare esclusivamente con quelli, ciò potrebbe anche aiutare.

+0

Molte grazie per la tua risposta. Ho dimenticato di menzionare che sto aprendo il matlabpool usando il profilo 'local' con 6 labs, tuttavia una singola iterazione è di circa 1,7 secondi usando la versione seriale, e la versione parallela non completa una singola iterazione in più di 5 minuti. – Hampycalc

+0

L'operazione (eig) non è multithread e non è necessario che sia la matrice acquisita come input solo 3x3. Tuttavia, trarrebbe vantaggio dall'assegnazione di ciascun ciclo esterno o interno a un nucleo separato. Tenterò la tua idea suggerita sull'estrazione della "parte locale" e riferirò. – Hampycalc

Problemi correlati