2015-04-17 14 views
8

Sto scrivendo il codice MATLAB per eseguire un integrale dimensionale 3:Perché la modalità batch è molto più veloce di parfor?

function [ fint ] = int3d_ser(R0, Rf, N) 
Nr = N; 
Nt = round(pi*N); 
Np = round(2*pi*N); 

rs = linspace(R0, Rf, Nr); 
ts = linspace(0, pi, Nt); 
ps = linspace(0, 2*pi, Np); 

dr = rs(2)-rs(1); 
dt = ts(2)-ts(1); 
dp = ps(2)-ps(1); 

C = 1/((4/3)*pi); 
fint = 0.0; 
for ir = 2:Nr 
    r = rs(ir); 
    r2dr = r*r*dr; 
    for it = 1:Nt-1 
    t = ts(it); 
    sintdt = sin(t)*dt; 
    for ip = 1:Np-1 
     p = ps(ip); 
     fint = fint + C*r2dr*sintdt*dp; 
    end 
    end 
end 

end 

per la versione associata int3d_par (parfor), apro una piscina MATLAB e basta sostituire il for con un parfor. Ottengo una velocità decente con l'esecuzione su più core (i miei test vanno da 2 a 8 core).

Tuttavia, quando si esegue la stessa integrazione in modalità batch con:

function [fint] = int3d_batch_cluster(R0, Rf, N, cluster, ncores) 

%%% note: This will not give back the same value as the serial or parpool version. 
%%%  If this was a legit integration, I would worry more about even dispersion 
%%%  of integration nodes per core, but I just want to benchmark right now so ... meh 

Nr = N; 
Nt = round(pi*N); 
Np = round(2*pi*N); 

rs = linspace(R0, Rf, Nr); 
ts = linspace(0, pi, Nt); 
ps = linspace(0, 2*pi, Np); 

dr = rs(2)-rs(1); 
dt = ts(2)-ts(1); 
dp = ps(2)-ps(1); 

C = 1/((4/3)*pi); 

rns = floor(Nr/ncores)*ones(ncores,1); 
RNS = zeros(ncores,1); 
for icore = 1:ncores 
    if(sum(rns) ~= Nr) 
    rns(icore) = rns(icore)+1; 
    end 
end 
RNS(1) = rns(1); 
for icore = 2:ncores 
    RNS(icore) = RNS(icore-1)+rns(icore); 
end 

rfs = rs(RNS); 
r0s = zeros(ncores,1); 
r0s(2:end) = rfs(1:end-1); 

j = createJob(cluster); 

for icore = 1:ncores 
    r0 = r0s(icore); 
    rf = rfs(icore); 
    rn = rns(icore); 
    trs = linspace(r0, rf, rn); 
    t{icore} = createTask(j, @int3d_ser, 1, {r0, rf, rn}); 
end 

submit(j); 
wait(j); 
fints = fetchOutputs(j); 

fint = 0.0; 
for ifint = 1:length(fints) 
    fint = fint + fints{ifint}; 
end 

end 

mi accorgo che è molto, molto più veloce. Perché fare questa integrazione in modalità batch è diverso rispetto a farlo in parfor?

Per riferimento, ho testare il codice con N da piccoli numeri come 10 e 20 (vedere la costante nella approssimazione polinomiale di esecuzione) a numeri più grandi come 1000 e 2000. Questo algoritmo scalare cubicly poiché assegno il numero dei nodi di integrazione nella direzione theta e phi per essere un multiplo costante di N specificato.

Per i nodi 2000, la versione parfor impiega circa 630 secondi, mentre lo stesso numero di nodi in modalità batch impiega circa 19 secondi (dove circa 12 secondi è semplicemente una comunicazione dall'alto che si ottiene anche per 10 nodi di integrazione).

+2

Quale 'for' stai sostituendo con un' parfor'? Potrebbe fare una differenza in quella struttura ad anello annidato (ad esempio colpendo il 'parfor' e incorrendo più volte nell'overhead di setup/demolizione del parallelismo, o facendo una struttura di slice non proprio ideale). Il codice della versione batch sembra avere "appiattito" la struttura del ciclo nidificato al momento in cui raggiunge le chiamate parallele (cioè, precalcolando i blocchi di input e facendo i cicli nidificati all'interno di ciascun blocco), che potrebbero spiegare il sovraccarico del parallelismo inferiore. –

+1

L'altra cosa è che, se capisco dove stai mettendo il 'parfor', la versione batch sta spostando molti meno dati attorno: passa solo i parametri' r0', 'rf' e' rn' le variabili intermedie vengono costruite localmente su ciascun worker, ma un 'parfor' all'interno di 'int3d_ser' causerebbe il marshalling di sottoinsiemi delle variabili temporanee create sul master e inviate a ciascun worker. –

+0

Es.cosa succede se si prende la funzione 'int3d_batch_cluster' e si sostituiscono le chiamate' createTask' con 'parfor icore = 1: ncores' attorno ad una normale chiamata di funzione a' int3d_ser'? Questo ti dirà se è il meccanismo "parfor" di per sé, o semplicemente come il tuo codice sta strutturando implicitamente i lotti di lavoro da inviare ai lavoratori. –

risposta

1

Dopo aver parlato con il supporto Mathworks, sembra che ho avuto un malinteso fondamentale su come funziona parfor. Avevo l'impressione che parfor si comportasse come openMP mentre la modalità batch si comportava come mpi in termini di memoria condivisa e distribuita.

Si scopre che parfor utilizza effettivamente anche una memoria distribuita. Quando creo, diciamo, 4 funzioni batch, il sovraccarico per la creazione di un nuovo processo avviene 4 volte. Ho pensato che l'utilizzo di un parfor avrebbe causato l'overhead solo 1 volta e che lo parfor avrebbe avuto luogo nello stesso spazio di memoria. Questo non è il caso.

Nel mio codice di esempio, risulta che per ogni iterazione di parfor, in realtà sto incoraggiando il sovraccarico della creazione di un nuovo thread. Quando si confrontano 'mele con mele', dovrei davvero creare lo stesso numero di chiamate batch come sono iterazioni nel ciclo parfor. Questo è il motivo per cui la funzione parfor impiegava molto più tempo - mi sono incorso nello molto più overheading per il multiprocessing.

+0

Ciò implica che la modalità batch sia più veloce di parfor? –