Implementazione parallela del calcolo dell’insieme di Mandelbrot
Davide Petturiti e-mail:
[email protected]
Applicazioni e Calcolo in Rete - Corso Avanzato Prof. A. Lagan`a, Prof. L. Pacifici – A.A. 2007/2008 — Corso di Laurea Specialistica in Informatica Facolt` a di Scienze Matematiche, Fisiche e Naturali
Universit`a degli Studi di Perugia
Indice 1 Presentazione del problema
1
2 Ambiente di sviluppo e di testing
4
3 Algoritmo sequenziale
5
4 Algoritmo parallelo con Static Load Balancing 4.1 Analisi di complessit` a e scalabilit` a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Valutazione e misurazione delle prestazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6 9 11
5 Algoritmo parallelo con Static Load Balancing e Remote Memory Access 13 5.1 Analisi di complessit` a e scalabilit` a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 5.2 Valutazione e misurazione delle prestazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 6 Algoritmo parallelo con Dynamic Load Balancing 17 6.1 Analisi di complessit` a e scalabilit` a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 6.2 Valutazione e misurazione delle prestazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 7 Conclusioni
25
Bibliografia e Sitografia
26
i
1
Presentazione del problema
Il presente lavoro ha lo scopo di fornire una implementazione parallela efficiente dell’algoritmo per il calcolo dell’insieme di Mandelbrot, perci` o, prima di esplorare le soluzioni proposte, `e bene definire formalmente il problema. L’insieme di Mandelbrot `e un insieme di punti nel piano complesso, la cui frontiera forma un frattale, cio`e un oggetto geometrico che si ripete nella sua struttura allo stesso modo su scale diverse: questa caratteristica `e spesso chiamata auto-similarit` a. Matematicamente, si pu` o dare la seguente definizione. Definizione 1.1 Data la funzione fc : C → C definita come fc (z) = z 2 + c dove c `e un parametro complesso, l’insieme di Mandelbrot `e il sottoinsieme del piano complesso dato da n M = c ∈ C : sup |fc (0)| < ∞ n∈N
ovvero per il quale la successione (fc (0), fc (fc (0)), fc (fc (fc (0))), · · ·) risulta essere non divergente. In sostanza, l’insieme di Mandelbrot `e un insieme di numeri complessi e pertanto ciascun numero complesso c pu` o appartenere o no a M . Computazionalmente, il problema pu` o essere caratterizzato in termini ricorsivi, grazie alla seguente equazione di ricorrenza zn+1
=
zn2 + c (1)
z0
=
0.
Si pu` o dimostrare che se per un dato n il modulo di zn ` e maggiore di 2 allora la successione diverger` a e quindi il punto c sar` a esterno all’insieme di Mandelbrot. Il minimo valore di n per cui |zn | > 2 `e un indice di quanto “lontano dal bordo” si trova un punto e viene spesso utilizzato per la visualizzazione a colori dell’insieme. In particolare, le iterazioni sono continuate fino a che il modulo di zn `e maggiore di 2 oppure fino al raggiungimento di un valore limite max. Graficamente, l’insieme di Mandelbrot viene rappresentato colorando di nero tutti i punti interni all’insieme ed assegnando un diverso colore ai punti esterni a seconda del numero di iterazioni eseguite per ciascun punto. D’ora in avanti quando parleremo di calcolo dell’insieme di Mandelbrot intenderemo il calcolo per ogni pixel dell’immagine (al quale corrisponde un determinato numero complesso c) del valore di n per cui |zn | > 2, il quale `e limitato superiormente da max. La Figura 1 mostra una possibile rappresentazione grafica dell’insieme di Mandelbrot. La struttura dati usata per il calcolo `e una matrice di interi di dimendione 1000×1000, dove ogni elemento corrisponde ad un pixel. In questo lavoro, l’output viene scritto come un file immagine .ps di dimensione 1000 × 1000 pixel, ed il calcolo avviene per gli assi immaginario e reale nell’intervallo [−2, 2] con il limite massimo di iterazioni fissato a 28 . Ricordando che per ogni z ∈ C, con z = a + bi p |z| = a2 + b2 1
Figura 1: Rappresentazione dell’insieme di Mandelbrot e che il quadrato di un numero complesso `e z 2 = a2 + 2abi + bi2 = a2 − b2 + 2abi, ciascuna iterazione della equazione di ricorrenza 1, pu`o essere calcolata come zrn+1
= zr2n − zi2n + cr
zin+1
=
(2) 2zrn zin + ci .
Il calcolo dell’insieme di Mandelbrot `e un tipico esempio di problema imbarazzantemente parallelo in quanto l’insieme di punti che deve essere calcolato pu`o essere partizionato ed assegnato a differenti processi e non `e richiesta nessuna comunicazione tra i processi per il calcolo del valore di ciascun punto. L’unica comunicazione richiesta `e quella per l’eventuale assegnamento dei lavori e per la raccolta dei risultati in un unico processo in modo da scrivere il file d’immagine. E’ bene precisare che il soggetto della parallelizzazione in questo lavoro `e il calcolo dell’insieme di Mandelbrot per ogni punto e non la scrittura del file d’immagine; per questo motivo, il tempo di scrittura del file d’immagine non viene preso in considerazione. Un altro importante aspetto da tenere presente `e che non `e noto in anticipo quante iterazioni sono richieste per ciascun punto della matrice: nello specifico, i punti nella Figura 1 di colore nero sono quelli che richiedono il numero massimo di iterazioni, pari a max, mentre i punti di colore pi` u chiaro richiedono molte meno iterazioni. L’osservazione precedente mostra l’importanza del Load Balancing nella parallelizzazione di questo problema, al fine di evitare che alcuni processori siano in idle mentre altri stanno ancora lavorando. Gli algoritmi proposti in questo lavoro si concentrano sostanzialmente sul bilanciamento del carico realizzando, rispettivamente: • Static Load Balancing; • Static Load Balancing con Accesso Remoto alla Memoria; • Dynamic Load Balancing.
2
Per quanto riguarda la distribuzione dinamica del carico, inoltre, l’agoritmo consente di specificare come parametro un fattore K di granularit` a della decomposizione. In ciascuno degli algoritmi prima citati, `e stata implementata una decomposizione bidimensionale della matrice di lavoro (che si suppone essere quadrata), considerando i processi organizzati in una griglia, anch’essa bidimensionale, di dimensione P × Q, con P e Q parametri. Tutti e tre gli algoritmi prevedono uno schema master/slave, con un processo master che si occupa dell’eventuale distribuzione del carico di lavoro e della raccolta e scrittura del file di immagine, ed un insieme di P × Q slave che si occupano della parte computazionale. Per quanto detto, i processi richiesti saranno in ogni caso P × Q + 1. Per ciascuno degli algoritmi sviluppati `e stata eseguita una analisi di complessit`a e scalabilit`a come in [2], inoltre per ognuno sono stati eseguiti diversi test variando le dimensioni P e Q della griglia di processi slave e constatando l’impatto sul tempo di esecuzione. In fine, si `e proceduto al calcolo di indicatori di performance come Speedup ed Efficienza che hanno messo in luce come l’algoritmo con Dynamic Load Balancing, congiuntamente ad un fattore di granularit` a K basso, garantisca le performance migliori.
3
2
Ambiente di sviluppo e di testing
La parte di sviluppo e debugging del codice `e stata eseguita su un cluster di pc, ClusterStudenti, costituito da 1 front-end e 5 nodi. Tale cluster `e stato costruito con vecchi pc, tutti con le medesime caratteristiche come `e riportato di seguito: • CPU AMD Athlon XP 1.8 GHz; • Hard Disk da 40 GB; • Sistema Operativo Debian GNU/Linux 4.0 “Etch”; • Rete Fast Ethernet; • Switch 10/100 Mbps. Purtroppo, il numero limitato dei nodi di ClusterStudenti non ha reso possibile eseguire su di esso la fase di testing, per questo tutti i test sono stati eseguiti su un Cluster Beowulf Intel, ChemGrid, costituito da 1 front-end e 8 nodi. Il front-end presenta la seguente configurazione: • CPU Intel Pentium 4 2.80GHz; • Sistema Operativo Fedora GNU/Linux Core 1 “Yarrow”; • Rete Gigabit Ethernet; mentre ciascuno degli 8 nodi `e caratterizzato da: • CPU Intel Pentium 4 Hyper-Threading 3 Ghz; • Sistema Operativo Fedora GNU/Linux Core 1 “Yarrow”; • Rete Gigabit Ethernet. Tutti gli algoritmi sono stati implementati usando la Message Passing Interface (MPI): in dettaglio, l’implementazione di MPI usata `e stata MPICH-2, con compilatore GNU C. Ogni test ` e stato ripetuto 50 volte ed ` e stato preso il valore medio dei singoli tempi come tempo di esecuzione, in modo da avere dei tempi rappresentativi e stabili.
4
3
Algoritmo sequenziale
L’algoritmo sequenziale consiste in due cicli annidati che consentono di scandire tutti gli elementi della matrice di interi usata per costruire l’immagine. Per ogni elemento della matrice vengono calcolate le coordinate del punto corrispondente nel sottoinsieme [−2, 2] × [−2, 2] ⊆ C (per semplicit`a di notazione consideriamo C isomorfo a R2 ) e si procede a calcolare la successione zn in modo da determinare il numero di iterazioni n per cui |zn | > 2. L’Algoritmo 1 riporta lo pseudocodice relativo all’algoritmo sequenziale. Algoritmo 1 Algoritmo sequenziale per il calcolo dell’insieme di Mandelbrot Input: Numero di righe e di colonne della matrice Output: Immagine dell’insieme di Mandelbrot 1: avvia timer 2: for i := 0 to numero righe − 1 do 3: for j := 0 to numero colonne − 1 do 4: cr := scala punto(i) 5: ci := scala punto(j) 6: immagine[i][j] := riempi pixel(cr , ci ) 7: end for 8: end for 9: f erma timer 10: scrivi immagine(immagine) L’Algoritmo 2, invece, riporta lo pseudocodice per il calcolo del numero di iterazioni per cui |zn | > 2 per un dato numero complesso c. Algoritmo 2 Algoritmo per il calcolo di n tale che |zn | > 2 Input: Numero complesso c Output: Numero di iterazioni n per cui si ha che |zn | > 2 1: zr := 0 2: zi := 0 3: for n := 0 to max − 1 do 4: if zr2 + zi2 > 4 then 5: return n 6: end if 7: zr := zr2 − zi2 + cr 8: zi := 2zr zi + ci 9: end for 10: return n Si `e gi` a avuto modo di sottolineare che il numero di iterazioni per ciascun pixel non `e noto a priori, anche se esso risulta essere limitato dal numero massimo di iterazioni max. Se indichiamo con n = 1000 × 1000 la dimensione del problema, il numero di iterazioni richieste dall’algoritmo sequenziale `e limitato superiormente da max × n per cui la complessit`a temporale nel caso pessimo sar` a O(n).
5
4
Algoritmo parallelo con Static Load Balancing
Abbiamo avuto modo di dire in precedenza che in questa sede si assume di lavorare con una griglia di P × Q processi slave. A questo proposito l’algoritmo SLB divide il calcolo in blocchi secondo tale griglia. Nello specifico, considerando immagini quadrate di dimensione N × N pixel, ciascuno dei processi della griglia ricever` a una sottomatrice di N/P righe e N/Q colonne. Nel caso particolare in cui N non sia divisibile per P e/o per Q, le restanti T = N %P righe e V = N %Q colonne verranno assegnate ai primi T processi di riga e ai primi V processi di colonna. La Figura 2 mostra un esempio di decomposizione ed assegnazione dei lavori, secondo lo schema proposto.
Figura 2: Esempio di decomposizione 2D per una griglia 2 × 4 nel caso di SLB Dal momento che ciascun processo nella griglia, conoscendo N , P e Q (che sono parametri comuni a tutti processi) pu` o desumere automaticamente il lavoro che deve compiere, si `e pensato di costruire una topologia virtuale cartesiana per contenere i processi slave in modo da ricalcare l’organizzazione in griglia bidimensionale. Grazie alla propria posizione nella topologia, quindi, ciascun processo slave sar`a in grado di sapere quale blocco calcolare, ed eventualmente quale riga e/o colonna rimanente. La soluzione proposta permette di evitare la comunicazione necessaria alla distribuzione degli intervalli di lavoro da parte del processo master ed in questo modo avere un minor tempo di esecuzione. Al fine di creare una topologia virtuale con i soli processi slave, `e necessario creare un intracomunicatore che non contenga il processo master: il Listato 1 riporta la funzione per la creazione del suddetto intracomunicatore. Listato 1: Funzione per la creazione dell’intracomunicatore senza il processo master void e x t r a c t m a s t e r ( ) { MPI Group g r o u p w o r l d , g r o u p w o r k e r s ; i n t master = MASTER; /∗ E s t r a e i l master d a l c o m u n i c a t o r e g l o b a l e ∗/ MPI Comm group (MPI COMM WORLD, &g r o u p w o r l d ) ; MPI Group excl ( g r o u p w o r l d , 1 , &master , &g r o u p w o r k e r s ) ; MPI Comm create (MPI COMM WORLD, g r o u p w o r k e r s , &MY COMM WORKERS) ; MPI Group free (& g r o u p w o r k e r s ) ; }
6
Il Listato 2, invece, riporta la funzione che si occupa di creare la topologia virtuale cartesiana. Listato 2: Funzione per la creazione della topologia virtuale cartesiana void c r e a t e 2 D g r i d ( ) { /∗ V a r i a b i l i p e r l a t o p o l o g i a c a r t e s i a n a ∗/ i n t ndims = 2 ; i n t dims [ 2 ] = {P , Q} ; int p e r i o d s [ 2 ] = {0 , 0 } ; int r e o r d e r = 1 ; /∗ Crea l a t o p o l o g i a c a r t e s i a n a ∗/ M P I C a r t c r e a t e (MY COMM WORKERS, ndims , dims , p e r i o d s , r e o r d e r , &MY COMM 2DGRID) ; MPI Comm rank (MY COMM 2DGRID, &myWorkerRank ) ; MPI Cart coords (MY COMM 2DGRID, myWorkerRank , 2 , c o o r d s ) ; }
Come si `e gi` a detto, ciascuno slave riesce a desumere automaticamente il suo lavoro in base alla sua posizione nella topologia virtuale ed il master si limita semplicemente a raccogliere i risultati dei calcoli. Tuttavia, i vari messaggi possono contenere rispettivamente una sottomatrice, una riga o una colonna. Per quanto detto, al fine di ricostruire propriamente l’immagine, il processo master utilizza dei tipi di dato derivati per rappresentare, rispettivamente, sottomatrici, righe e colonne. Inoltre, in fase di ricezione, il master pu`o ricevere in modo non prevedibile le tre diverse tipologie di messaggio, quindi `e necessario andare a testare il messaggio in arrivo con la funzione MPI Probe che permette di stabilire che tipo di messaggio si sta per ricevere. La distinzione tra i vari tipi di messaggio avviene utilizzando tag diversi, inoltre, il tag `e usato anche per indicare il contatore del blocco di dati (sottomatrice, riga o colonna) inviato, risparmiando cos`ı l’invio di un messaggio. In dettaglio, il contatore del blocco viene sommato al valore che indica il tipo di messaggio, il quale funge da offset. Per evitare confusioni, la differenza tra due qualsiasi dei valori usati per specificare il tipo di messaggio `e scelta molto maggiore del numero totale di possibili sottomatrici, righe e colonne. Il lavoro del master e di ciascuno slave `e raccolto in due diverse funzioni: il Listato 3 riporta la funzione che racchiude il lavoro del master mentre il Listato 4 riporta quella che contiene il lavoro dello slave. Listato 3: Funzione che contiene il lavoro del master nel caso SLB void master work ( ) { i n t image [ SIZE ] [ SIZE ] ; i n t r , c , s o u r c e , tag , r e c e i v e d = 0 ; MPI Datatype MY SUBROW, MY SUBMATRIX, MY ROW, MY COL; MPI Status s t a t u s ; double s t a r t t i m e , e n d t i m e ; /∗ Prende i l tempo d i i n i z i o ∗/ s t a r t t i m e = MPI Wtime ( ) ; /∗ D e f i n i s c e i l t i p o d i d a t o s o t t o m a t r i c e ∗/ MPI Type vector ( n c o l s , 1 , 1 , MPI INT , &MY SUBROW) ; MPI Type hvector ( nrows , 1 , SIZE ∗ s i z e o f ( i n t ) , MY SUBROW, &MY SUBMATRIX) ; MPI Type commit(&MY SUBMATRIX) ; /∗ D e f i n i s c e i l t i p o d i d a t o r i g a ∗/ MPI Type contiguous ( SIZE , MPI INT , &MYROW) ; MPI Type commit(&MYROW) ; /∗ D e f i n i s c e i l t i p o d i d a t o c o l o n n a ∗/ MPI Type vector ( SIZE , 1 , SIZE , MPI INT , &MY COL ) ; MPI Type commit(&MY COL ) ; while ( r e c e i v e d < ( ( P ∗ Q) + erows + e c o l s ) ) { /∗ I n t e r c e t t a i l m e s s a g g i o i n a r r i v o ∗/
7
MPI Probe (MPI ANY SOURCE, MPI ANY TAG, MPI COMM WORLD, &s t a t u s ) ; i f ( s t a t u s . MPI TAG < ROW TAG) { r = nrows ∗ ( s t a t u s . MPI TAG / Q) ; c = n c o l s ∗ ( s t a t u s . MPI TAG % Q) ; MPI Recv(&image [ r ] [ c ] , 1 , MY SUBMATRIX, s t a t u s . MPI SOURCE, s t a t u s . MPI TAG, MPI COMM WORLD, &s t a t u s ) ; } e l s e i f ( s t a t u s . MPI TAG >= ROW TAG && s t a t u s . MPI TAG < COL TAG) { r = ( nrows ∗ P) + ( s t a t u s . MPI TAG − ROW TAG) ; MPI Recv(&image [ r ] [ 0 ] , 1 , MY ROW, s t a t u s . MPI SOURCE, s t a t u s . MPI TAG, MPI COMM WORLD, &s t a t u s ) ; } e l s e i f ( s t a t u s . MPI TAG >= COL TAG) { c = ( n c o l s ∗ Q) + ( s t a t u s . MPI TAG − COL TAG ) ; MPI Recv(&image [ 0 ] [ c ] , 1 , MY COL, s t a t u s . MPI SOURCE, s t a t u s . MPI TAG, MPI COMM WORLD, &s t a t u s ) ; } r e c e i v e d ++; } /∗ L i b e r a i t i p i d i d a t o d e r i v a t i ∗/ MPI Type free (&MY SUBMATRIX) ; MPI Type free (&MYROW) ; MPI Type free (&MY COL ) ; /∗ Prende i l tempo d i f i n e ∗/ e n d t i m e = MPI Wtime ( ) ; /∗ S c r i v e i l f i l e d e l l ’ immagine ∗/ w r i t e i m a g e ( image ) ; }
Listato 4: Funzione che contiene il lavoro dello slave nel caso SLB void s l a v e w o r k ( ) { double x0 , y0 , x i n c , y i n c ; i n t i , j , r , c , b u f f e r [ nrows ] [ n c o l s ] , b u f f e r 2 [ SIZE ] ; i n t d e s t = 0 , t a g = myWorkerRank ; /∗ C a l c o l a i v a l o r i d e l b l o c c o ∗/ y0 = −2.0; x0 = −2.0; x i n c = 4 . 0 / SIZE ; y i n c = 4 . 0 / SIZE ; f o r ( i = 0 ; i < nrows ; i ++){ f o r ( j = 0 ; j < n c o l s ; j ++){ r = ( c o o r d s [ 0 ] ∗ nrows ) + i ; c = ( coords [ 1 ] ∗ ncols ) + j ; b u f f e r [ i ] [ j ] = p i x v a l ( x0 + r ∗ x i n c , y0 + c ∗ y i n c ) ; } } /∗ I n v i a i l b l o c c o ∗/ MPI Send ( b u f f e r , nrows ∗ n c o l s , MPI INT , d e s t , tag , MPI COMM WORLD) ; /∗ I n v i a l a r i g a i n e c c e s s o (SOLO s e n e c e s s a r i o ) ∗/ i f ( c o o r d s [ 0 ] < erows && c o o r d s [ 1 ] == 0 ) { f o r ( i = 0 ; i < SIZE ; i ++){ r = (P ∗ nrows ) + c o o r d s [ 0 ] ; c = i; b u f f e r 2 [ i ] = p i x v a l ( x0 + r ∗ x i n c , y0 + c ∗ y i n c ) ; ; } MPI Send ( b u f f e r 2 , SIZE , MPI INT , d e s t , ( t a g / Q) + ROW TAG, MPI COMM WORLD) ; }
8
/∗ I n v i a l a c o l o n n a i n e c c e s s o (SOLO s e n e c e s s a r i o ) ∗/ i f ( c o o r d s [ 1 ] < e c o l s && c o o r d s [ 0 ] == 0 ) { f o r ( i = 0 ; i < SIZE ; i ++){ r = i; c = (Q ∗ n c o l s ) + c o o r d s [ 1 ] ; b u f f e r 2 [ i ] = p i x v a l ( x0 + r ∗ x i n c , y0 + c ∗ y i n c ) ; ; } MPI Send ( b u f f e r 2 , SIZE , MPI INT , d e s t , ( t a g % Q) + COL TAG, MPI COMM WORLD) ; } }
4.1
Analisi di complessit` a e scalabilit` a
La matrice di lavoro ha dimensione n = N × N , mentre gli slave sono s = P × Q per un totale di p = s + 1 processi (compreso il master). Ogni slave desume dalla propria posizione nella topologia virtuale cartesiana gli estremi della sottomatrice da calcolare, che `e costituita per ciascuno da u = N/P righe e v = N/Q colonne. Inoltre, nel caso in cui N non sia divisibile per P e/o per Q i primi r = N %P processi slave di riga ed i primi c = N %Q processi slave di colonna dovranno occuparsi, rispettivamente, di calcolare ciascuno la riga e/o la colonna in eccesso corrispondente. Ricordiamo che non `e noto a priori il numero di iterazioni per ciascun elemento della matrice, tuttavia possiamo determinare un upper bound considerando che nel caso pessimo vengono eseguite al pi` u max iterazioni, il cui tempo corrispondente sar` a quindi tmax . In base alle osservazioni appena fatte, il tempo totale relativo alla computazione pu`o essere espresso come Tcomp ≤ stmax uv + (r + c)tmax N. Una volta eseguito il proprio lavoro, ciascuno slave invier`a un messaggio di dimensione u × v al master e, nel caso r 6= 0 e/o c 6= 0, i primi r processi di riga e/o i primi c processi slave di colonna, invieranno anche un messaggio di dimensione N pari, cio`e, ad una intera riga o colonna. Il tempo totale relativo alla comunicazione `e espresso, invece, dalla seguente Tcomm = s(ts + tw uv) + (r + c)(ts + tw N ). In conclusione il tempo di esecuzione parallelo pu`o essere formulato come segue Tp ≤
stmax uv + (r + c)tmax N + s(ts + tw uv) + (r + c)(ts + tw N ) . p
(3)
Tale espressione si semplifica notevolmente nel caso in cui N `e divisibile sia per P che per Q (ovvero quando r = c = 0), diventando Tp ≤
n stmax uv + s(ts + tw uv) ≤ tmax uv + ts + tw uv = O . s+1 s
E’ possibile notare come il tempo relativo alla comunicazione dipenda da due parametri, ts e tw , caratteristici dell’ambiente di esecuzione adottato. Come riportato in [2], infatti, il tempo di invio di un messaggio pu`o essere espresso come funzione della dimensione del messaggio nel modo che segue Tmsg (d) = ts + tw d dove ts indica il tempo di startup, tw il tempo per trasferire una singola word e d indica la variabile che specifica la dimensione del messaggio in numero di word. 9
Figura 3: Retta di regressione dei tempi di invio messaggio Al fine di fornire una stima dei parametri ts e tw caratteristici dell’ambiente di esecuzione usato (ovvero il cluster ChemGrid), `e stato scritto un piccolo programma di benchmark che misura il tempo necessario all’invio di un messaggio per dimensioni via via crescenti. Tale programma, per ciascuna dimensione del messaggio, esegue 1000 prove in modo da avere dei valori stabili. I valori raccolti sono poi stati elaborati con il software di statistica R, che ci ha consentito di determinare facilmente la retta di regressione dalla quale si ricava tw come il coefficiente angolare e ts come l’intercetta. I valori ottenuti dal modello di regressione lineare sono riportati di seguito ts = 6.186µs tw = 0.05534µs. La Figura 3 riporta il grafico della retta di regressione ricavata dai tempi raccolti. Utilizzando un altro semplice programma di benchmark `e stato possibile stimare il tempo relativo all’esecuzione del numero massimo di iterazioni in un pixel, ovvero tmax , il quale `e risultato essere tmax = 22µs. Ovviamente, il modello fornito costituisce un upper bound del tempo reale di esecuzione. Come suggerito in [2], si pu` o ottenere un modello di performance pi` u realistico considerando come tempo di computazione per ciascun punto della matrice, non il tempo nel caso pessimo, ovvero tmax , ma il tempo medio, ovvero tc . Ancora una volta, il parametro tc `e stato stimato come segue tc = 14µs. Per quanto detto, il modello 3 pu` o essere modificato nel modo seguente Tp =
stc uv + (r + c)tc N + s(ts + tw uv) + (r + c)(ts + tw N ) p
(4)
e, nel caso in cui r = c = 0, si riduce a Tp =
stc uv + s(ts + tw uv) . p 10
(5)
A questo punto, essendo T1 = tc n, `e possibile esprimere l’Efficienza come Ep =
4.2
tc n tc n = . stc uv + s(ts + tw uv) tc n + sts + tw n
(6)
Valutazione e misurazione delle prestazioni
Sono stati eseguiti pi` u test con valori variabili di P e Q in modo da osservare l’impatto delle dimensioni della griglia di slave sulle performace; i test sono stati ripetuti su problemi di dimensione 1000 × 1000 (ovvero N 2 ), 1414 × 1414 (ovvero 2N 2 ), 707 × 707 (ovvero N 2 /2) e 500 × 500 (ovvero N 2 /4). La Tabella 1 riporta i valori osservati per la dimensione del problema 1000 × 1000 mentre, per motivi di spazio, non riportiamo le tabelle relative alle altre dimensioni. Griglia (P × Q) seriale 1×2 2×1 2×3 3×2 2×4 4×2
Processi (P × Q + 1) 1 3 3 7 7 9 9
Tp 2.811830 1.818810 1.386350 1.092570 1.602050 0.835635 0.910653
Sp 1 1.55 2.03 2.57 1.76 3.36 3.09
Ep 1 0.52 0.68 0.37 0.25 0.37 0.34
Tabella 1: Valori osservati per l’algoritmo con SLB con dimensione del problema 1000 × 1000 Le Figure 4 e 5 mostrano i grafici relativi ai dati osservati per le diverse dimensioni del problema testate.
Figura 4: Grafico dello Speedup dell’algoritmo con SLB Nella Tabella 1 si possono osservare delle performance diverse a parit`a di numero totale di processi, in relazione a diverse griglie, questo perch`e in base alla griglia scelta varier`a anche la distribuzione del carico. E’ bene tenere presente che le griglie per cui N non risulta divisibile per P e/o per Q avranno un overhead dovuto al calcolo e al trasferimento delle righe e/o colonne in eccesso con conseguente aumento del tempo di esecuzione e diminuzione dello Speedup. 11
Figura 5: Grafico dell’Efficienza dell’algortimo con SLB Precisiamo che, per il disegno dei grafici, a parit` a del numero totale di processi, ` e stata scelta la griglia che garantisce il minor tempo di esecuzione.
12
5
Algoritmo parallelo con Static Load Balancing e Remote Memory Access
Il seguente algoritmo riprende l’idea base dell’algoritmo SLB, modificando, tuttavia, il pattern di comunicazione in modo da utilizzare l’accesso remoto alla memoria il quale consente di separare lo scambio di dati dalla sincronizzazione. Come sottolineato in [1], l’RMA pu` o comportare un miglioramento delle performance in quei sistemi con un supporto hardware alle operazioni in memoria remota, in particolar modo quando ci sono molte operazioni RMA tra una sincronizzazzione e l’altra. Un altro fatto da non sottovalutare usando l’RMA `e lo spostamento della comunicazione verso una sola delle parti coinvolte con, in genere, una semplificazione anche in termini di codice. Come noto, MPI-2 ha aggiunto l’RMA allo standard MPI e l’intero meccanismo si basa sul concetto di finestra, ovvero di area di memoria che ciascun processo pu`o dedicare all’accesso remoto. Per quanto riguarda l’algoritmo con SLB precedente, l’RMA si rivela come una naturale estensione poich`e i diversi processi slave desumono il loro lavoro in base alla loro posizione nella topologia virtuale cartesiana e ciascuno accede in scrittura ad aree diverse di una stessa struttura dati. L’idea `e che il processo master crei una finestra corrispondete all’intera matrice usata per costruire l’immagine e che ciascun processo slave depositi i risultati dei propri calcoli nella rispettiva area della finestra. E’ noto che le primitive di comunicazione RMA non sono bloccanti, quindi si rende necessario un meccanismo esplicito di sincronizzazione, che assicuri al master di utilizzare una matrice con dati veritieri. Nel nostro caso `e stata scelta una comunicazione di tipo Active Target Comunication nella quale, cio`e, sia il processo master che lo slave sono coinvolti nella comunicazione, proprio per l’esigenza del master di sapere quando la matrice `e pronta per la scrittura su file. Nello specifico, la sincronizzazione `e gestita mediante la primitiva MPI Fence che finalizza tutte le chiamate RMA successive ad una precedente chiamata di MPI Fence. Il master si limiter` a, quindi, solo a creare una finestra pari all’intera matrice e a chiamare due volte la primitiva MPI Fence, dopo di che si potr` a dedicare all scrittura del file di immagine. Il Listato 5 riporta la funzione che contiene il lavoro del processo master. Listato 5: Funzione che contiene il lavoro del master nel caso SLB con RMA void master work ( ) { i n t image [ SIZE ] [ SIZE ] ; double s t a r t t i m e , e n d t i m e ; /∗ Prende i l tempo d i i n i z i o ∗/ s t a r t t i m e = MPI Wtime ( ) ; /∗ Crea l a f i n e s t r a ∗/ MPI Win create ( image , SIZE∗SIZE∗ s i z e o f ( i n t ) , s i z e o f ( i n t ) , MPI INFO NULL , MPI COMM WORLD, &win ) ; /∗ S i n c r o n i z z a z i o n e ∗/ MPI Win fence ( 0 , win ) ; // // LAVORO DEGLI SLAVE // /∗ S i n c r o n i z z a z i o n e ∗/ MPI Win fence ( 0 , win ) ; /∗ L i b e r a l a f i n e s t r a ∗/ MPI Win free(&win ) ; /∗ Prende i l tempo d i f i n e ∗/ e n d t i m e = MPI Wtime ( ) ; /∗ S c r i v e i l f i l e d e l l ’ immagine ∗/ w r i t e i m a g e ( image ) ; }
13
Per quanto riguarda gli slave, invece, essi non dovranno far altro che calcolare la sottomatrice relativa ed eventualmente la riga e/o la colonna in eccesso e procedere alla scrittura nella finestra del master, tutto questo tra le due chiamate di MPI Fence. Il Listato 6 riporta la funzione che contiene il lavoro dei processi slave. Listato 6: Funzione che contiene il lavoro dello slave nel caso SLB con RMA void s l a v e w o r k ( ) { double x0 , y0 , x i n c , y i n c ; i n t i , j , r , c , b u f f e r [ nrows ] [ n c o l s ] , b u f f e r 2 [ SIZE − e c o l s ] , b u f f e r 3 [ SIZE ] ; int dest = 0 , disp ; MPI Datatype MY SUBROW, MY SUBMATRIX, MY ROW, MY COL; /∗ D e f i n i s c e i l t i p o d i d a t o s o t t o m a t r i c e ∗/ MPI Type vector ( n c o l s , 1 , 1 , MPI INT , &MY SUBROW) ; MPI Type hvector ( nrows , 1 , SIZE ∗ s i z e o f ( i n t ) , MY SUBROW, &MY SUBMATRIX) ; MPI Type commit(&MY SUBMATRIX) ; /∗ D e f i n i s c e i l t i p o d i d a t o r i g a ∗/ MPI Type contiguous ( SIZE − e c o l s , MPI INT , &MYROW) ; MPI Type commit(&MYROW) ; /∗ D e f i n i s c e i l t i p o d i d a t o c o l o n n a ∗/ MPI Type vector ( SIZE , 1 , SIZE , MPI INT , &MY COL ) ; MPI Type commit(&MY COL ) ; /∗ Crea l a f i n e s t r a ∗/ MPI Win create (MPI BOTTOM, 0 , s i z e o f ( i n t ) , MPI INFO NULL , MPI COMM WORLD, &win ) ; // /∗ S i n c r o n i z z a z i o n e ∗/ MPI Win fence ( 0 , win ) ; // /∗ C a l c o l a i v a l o r i d e l b l o c c o ∗/ y0 = −2.0; x0 = −2.0; x i n c = 4 . 0 / SIZE ; y i n c = 4 . 0 / SIZE ; f o r ( i = 0 ; i < nrows ; i ++){ f o r ( j = 0 ; j < n c o l s ; j ++){ r = ( c o o r d s [ 0 ] ∗ nrows ) + i ; c = ( coords [ 1 ] ∗ ncols ) + j ; b u f f e r [ i ] [ j ] = p i x v a l ( x0 + r ∗ x i n c , y0 + c ∗ y i n c ) ; } } /∗ I n v i a i l b l o c c o ∗/ d i s p = c o o r d s [ 0 ] ∗ nrows ∗ SIZE + c o o r d s [ 1 ] ∗ n c o l s ; MPI Put ( b u f f e r , nrows ∗ n c o l s , MPI INT , d e s t , d i s p , 1 , MY SUBMATRIX, win ) ; /∗ I n v i a l a r i g a i n e c c e s s o (SOLO s e n e c e s s a r i o ) ∗/ i f ( c o o r d s [ 0 ] < erows && c o o r d s [ 1 ] == 0 ) { f o r ( i = 0 ; i < SIZE − e c o l s ; i ++){ r = (P ∗ nrows ) + c o o r d s [ 0 ] ; c = i; b u f f e r 2 [ i ] = p i x v a l ( x0 + r ∗ x i n c , y0 + c ∗ y i n c ) ; } d i s p = (P ∗ nrows ∗ SIZE ) + c o o r d s [ 0 ] ∗ SIZE ; MPI Put ( b u f f e r 2 , SIZE − e c o l s , MPI INT , d e s t , d i s p , 1 , MY ROW, win ) ; } /∗ I n v i a l a c o l o n n a i n e c c e s s o (SOLO s e n e c e s s a r i o ) ∗/ i f ( c o o r d s [ 1 ] < e c o l s && c o o r d s [ 0 ] == 0 ) { f o r ( i = 0 ; i < SIZE ; i ++){ r = i; c = (Q ∗ n c o l s ) + c o o r d s [ 1 ] ;
14
b u f f e r 3 [ i ] = p i x v a l ( x0 + r ∗ x i n c , y0 + c ∗ y i n c ) ; } disp = Q ∗ ncols + coords [ 1 ] ; MPI Put ( b u f f e r 3 , SIZE , MPI INT , d e s t , d i s p , 1 , MY COL, win ) ; } /∗ L i b e r a i t i p i d i d a t o d e r i v a t i ∗/ MPI Type free (&MY SUBMATRIX) ; MPI Type free (&MYROW) ; MPI Type free (&MY COL ) ; // /∗ S i n c r o n i z z a z i o n e ∗/ MPI Win fence ( 0 , win ) ; // /∗ L i b e r a l a f i n e s t r a ∗/ MPI Win free(&win ) ; }
5.1
Analisi di complessit` a e scalabilit` a
Per quanto riguarda la complessit` a, si pu` o adottare il modello sviluppato per l’algoritmo con SLB nel quale, per` o, varieranno i tempi di comunicazione in quanto, in questo caso, varia sostanzialmente il meccanismo di trasferimento di dati. Tendenzialmente, l’uso dell’RMA porta un miglioramento delle performance quando ci sono dei trasferimenti di dati, multipli e di entit` a limitata tra due diverse sincronizzazioni. Nel nostro caso, essendo il numero di processi limitato dall’ambiente di testing (che `e costituito da 9 processori fisici), di fatto abbiamo pochi trasferimenti di dati e di entit`a abbastanza consistente che avvengono tra le due sole chiamate di MPI Fence. Quanto detto ci fa intuire che non potremo aspettarci un aumento delle performance, anche perch`e l’ambiente di testing usato non dispone di supporto hardware per operazioni di memoria remota.
5.2
Valutazione e misurazione delle prestazioni
Anche in questo caso, `e stata eseguita una serie di test con diversi valori di P e Q e con dimensione del problema 1000 × 1000, 1414 × 1414, 707 × 707 e 500 × 500. La Tabella 2 riporta i valori osservati per le diverse griglie di processi slave nel caso 1000 × 1000 (per motivi di spazio non riportiamo le altre tabelle). Griglia (P × Q) seriale 1×2 2×1 2×3 3×2 2×4 4×2
Processi (P × Q + 1) 1 3 3 7 7 9 9
Tp 2.791620 1.850920 1.386160 1.130400 1.744400 0.898152 0.963843
Sp 1 1.55 2.03 2.57 1.76 3.36 3.09
Ep 1 0.52 0.68 0.37 0.25 0.37 0.34
Tabella 2: Valori osservati per l’algoritmo con SLB e RMA con dimensione del problema 1000 × 1000 Le Figure 6 e 7 riportano i grafici, rispettivamente dello Speedup e dell’Efficienza: anche in questo caso per ciascun numero totale di processi `e stata scelta la griglia di slave con le performance migliori. Come gi` a detto nella Sottosezione 5.1, l’uso dell’RMA per l’algoritmo con SLB non consente un aumento delle performance: in effetti, la Tabella 2 mette in luce come tutti i tempi di esecuzione siano leggermente peggiori rispetto all’algoritmo con message-passing. E’ per` o possibile apprezzare una semplificazione a livello di codice, soprattutto per quanto riguarda il lavoro del master. 15
Figura 6: Grafico dello Speedup dell’algoritmo con SLB e RMA
Figura 7: Grafico dell’Efficienza dell’algoritmo con SLB e RMA
16
6
Algoritmo parallelo con Dynamic Load Balancing
E’ stato precedentemente sottolineato come non sia possibile conoscere a priori il numero di iterazioni da compiere per ciascun punto della matrice di lavoro: questo rende particolarmente importante un buono schema di bilanciamento del carico tra i vari processi. Nello specifico, i punti che richiederanno il massimo numero di iterazioni sono quelli che nell’immagine finale risulteranno colorati di nero, cio`e i punti appartenenti all’insieme di Mandelbrot. I due precedenti algortimi hanno messo in luce l’inefficienza della distribuzione statica del carico per questo tipo di problema, proprio perch`e la matrice `e divisa in blocchi grandi i quali possono anche non contenere affatto le aree di colore nero dell’immagine (ovvero le parti di maggior dispendio computazionale). L’algoritmo con distribuzione dinamica del carico nasce proprio per superare il limite prima esposto e a questo proposito considera un fattore di granularit`a K ∈]0, 1] che consente di aumentare o diminuire la grana dei blocchi in cui `e partizionata la matrice. In dettaglio, indicando ancora con P × Q la griglia di processi slave, la matrice sar`a suddivisa in blocchi di K × N/P righe e K × N/Q colonne, i quali verranno distribuiti orizzontalmente tra gli slave. La Figura 8 mostra una possibile decomposizione dell’immagine, secondo lo schema proposto.
Figura 8: Esempio di decomposizione 2D per una griglia 2 × 4 con K = 0.5 nel caso di DLB Anche in questo caso, occorre considerare le eventuali righe e colonne in eccesso che dovranno comunque essere assegnate. In questo algoritmo il master si occupa di distribuire il lavoro a ciascuno slave, realizzando un task farm dinamico; quando uno slave ha terminato il suo lavoro, esso invia il risultato della sua computazione al master, il quale provvede ad assegnare, se disponibile, nuovo lavoro, altrimenti invia un segnale di stop. Se vengono scelte delle unit` a di lavoro di dimensione ridotta (ovvero K `e vicino a 0), allora si avr` a una riduzione del tempo di esecuzione. Infatti, anche se esiste una grande differenza del lavoro richiesto tra i vari blocchi ed uno slave spende molto tempo su un singolo blocco, gli altri slave possono continuare a richiedere pi` u lavoro al master, evitando la condizione di idle. Anche in questo caso verranno utilizzati dei tipi di dato derivati per rappresentare, rispettivamente, sottomatrici, righe e colonne. La distinzione tra i diversi tipi di messaggio avver`a grazie al campo tag, usato anche per specificare l’indice del blocco di dati inviato, nello stesso modo dell’algoritmo SLB. Il Listato 7 riporta il codice relativo al lavoro del master, mentre il Listato 8 riporta quello dello slave.
17
Listato 7: Funzione che contiene il lavoro del master nel caso DLB void master work ( ) { i n t image [ SIZE ] [ SIZE ] ; int p , r , c , source , dest , tag ; MPI Datatype MY SUBROW, MY SUBMATRIX, MY ROW, MY COL; MPI Status s t a t u s ; double s t a r t t i m e , e n d t i m e ; int n e x t b l o c k = 0 , next row = 0 , n e x t c o l = 0 , remaining = 0 ; /∗ Prende i l tempo d i i n i z i o ∗/ s t a r t t i m e = MPI Wtime ( ) ; /∗ D e f i n i s c e i l t i p o d i d a t o s o t t o m a t r i c e ∗/ MPI Type vector ( n c o l s , 1 , 1 , MPI INT , &MY SUBROW) ; MPI Type hvector ( nrows , 1 , SIZE ∗ s i z e o f ( i n t ) , MY SUBROW, &MY SUBMATRIX) ; MPI Type commit(&MY SUBMATRIX) ; /∗ D e f i n i s c e i l t i p o d i d a t o r i g a ∗/ MPI Type contiguous ( SIZE , MPI INT , &MYROW) ; MPI Type commit(&MYROW) ; /∗ D e f i n i s c e i l t i p o d i d a t o c o l o n n a ∗/ MPI Type vector ( SIZE , 1 , SIZE , MPI INT , &MY COL ) ; MPI Type commit(&MY COL ) ; /∗ S i assume una d i s t r i b u z i o n e p r e RIGHE d e i b l o c c h i ∗/ /∗ Assegna l a prima t r a n c e d i b l o c c h i ∗/ f o r ( p = 1 ; p < numProc ; p++){ MPI Send(& n e x t b l o c k , 1 , MPI INT , p , BLOCK TAG, MPI COMM WORLD) ; n e x t b l o c k ++; r e m a i n i n g ++; } /∗ R i c e v e l a r i s p o s t a d e i worker ed a s s e g n a nuovo l a v o r o ∗/ while ( r e m a i n i n g > 0 ) { /∗ I n t e r c e t t a i l m e s s a g g i o i n a r r i v o ∗/ MPI Probe (MPI ANY SOURCE, MPI ANY TAG, MPI COMM WORLD, &s t a t u s ) ; /∗ I l m e s s a g g i o da r i c e v e r e e ’ un b l o c c o ∗/ i f ( s t a t u s . MPI TAG < ROW TAG) { r = nrows ∗ ( ( s t a t u s . MPI TAG − BLOCK TAG) / b l o c k s o n r o w ) ; c = n c o l s ∗ ( ( s t a t u s . MPI TAG − BLOCK TAG) % b l o c k s o n r o w ) ; MPI Recv(&image [ r ] [ c ] , 1 , MY SUBMATRIX, s t a t u s . MPI SOURCE, s t a t u s . MPI TAG, MPI COMM WORLD, &s t a t u s ) ; } e l s e i f ( s t a t u s . MPI TAG >= ROW TAG && s t a t u s . MPI TAG < COL TAG) { r = ( nrows ∗ b l o c k s o n c o l ) + ( s t a t u s . MPI TAG − ROW TAG) ; MPI Recv(&image [ r ] [ 0 ] , 1 , MY ROW, s t a t u s . MPI SOURCE, s t a t u s . MPI TAG, MPI COMM WORLD, &s t a t u s ) ; } e l s e i f ( s t a t u s . MPI TAG >= COL TAG) { c = ( n c o l s ∗ b l o c k s o n r o w ) + ( s t a t u s . MPI TAG − COL TAG ) ; MPI Recv(&image [ 0 ] [ c ] , 1 , MY COL, s t a t u s . MPI SOURCE, s t a t u s . MPI TAG, MPI COMM WORLD, &s t a t u s ) ; } re maining −−; /∗ Assegna nuovo l a v o r o s e ce n ’ e ’ ∗/ d e s t = s t a t u s . MPI SOURCE ; /∗ I n v i a un b l o c c o ∗/ i f ( next block < blocks on row ∗ b l o c k s o n c o l ){ MPI Send(& n e x t b l o c k , 1 , MPI INT , d e s t , BLOCK TAG, MPI COMM WORLD) ; n e x t b l o c k ++; r e m a i n i n g ++; }
18
/∗ I n v i a una r i g a i n e c c e s s o ∗/ e l s e i f ( n e x t r o w < erows ) { MPI Send(& next row , 1 , MPI INT , d e s t , ROW TAG, MPI COMM WORLD) ; n e x t r o w++; r e m a i n i n g ++; } /∗ I n v i a una c o l o n n a i n e c c e s s o ∗/ else i f ( n e x t c o l < e c o l s ){ MPI Send(& n e x t c o l , 1 , MPI INT , d e s t , COL TAG, MPI COMM WORLD) ; n e x t c o l ++; r e m a i n i n g ++; } else { MPI Send(& n e x t c o l , 1 , MPI INT , d e s t , STOP TAG, MPI COMM WORLD) ; } } /∗ L i b e r a i t i p i d i d a t o d e r i v a t i ∗/ MPI Type free (&MY SUBMATRIX) ; MPI Type free (&MYROW) ; MPI Type free (&MY COL ) ; /∗ Prende i l tempo d i f i n e ∗/ e n d t i m e = MPI Wtime ( ) ; /∗ S c r i v e i l f i l e d e l l ’ immagine ∗/ w r i t e i m a g e ( image ) ; }
Listato 8: Funzione che contiene il lavoro dello slave nel caso DLB void s l a v e w o r k ( ) { double x0 , y0 , x i n c , y i n c ; i n t i , j , r , c , b u f f e r [ nrows ] [ n c o l s ] , b u f f e r 2 [ SIZE ] ; i n t d e s t = MASTER, tag , myWork ; int end of work = 0 ; MPI Status s t a t u s ; y0 = −2.0; x0 = −2.0; x i n c = 4 . 0 / SIZE ; y i n c = 4 . 0 / SIZE ; /∗ C a l c o l a i v a l o r i d e l b l o c c o ∗/ while ( ! e n d o f w o r k ) { MPI Recv(&myWork , 1 , MPI INT , MASTER, MPI ANY TAG, MPI COMM WORLD, &s t a t u s ) ; i f ( s t a t u s . MPI TAG == STOP TAG) { end of work = 1; continue ; } t a g = myWork ; /∗ C a l c o l a i l b l o c c o e l o i n v i a ∗/ i f ( s t a t u s . MPI TAG == BLOCK TAG) { f o r ( i = 0 ; i < nrows ; i ++){ f o r ( j = 0 ; j < n c o l s ; j ++){ r = ( nrows ∗ (myWork / b l o c k s o n r o w ) ) + i ; c = ( n c o l s ∗ (myWork % b l o c k s o n r o w ) ) + j ; b u f f e r [ i ] [ j ] = p i x v a l ( x0 + r ∗ x i n c , y0 + c ∗ y i n c ) ; } } MPI Send ( b u f f e r , nrows ∗ n c o l s , MPI INT , d e s t , t a g + BLOCK TAG, MPI COMM WORLD) ; }
19
/∗ C a l c o l a l a r i g a e l a i n v i a ∗/ e l s e i f ( s t a t u s . MPI TAG == ROW TAG) { f o r ( i = 0 ; i < SIZE ; i ++){ r = ( nrows ∗ b l o c k s o n c o l ) + myWork ; c = i; b u f f e r 2 [ i ] = p i x v a l ( x0 + r ∗ x i n c , y0 + c ∗ y i n c ) ; } MPI Send ( b u f f e r 2 , SIZE , MPI INT , d e s t , t a g + ROW TAG, MPI COMM WORLD) ; } /∗ C a l c o l a l a c o l o n n a e l a i n v i a ∗/ e l s e i f ( s t a t u s . MPI TAG == COL TAG) { f o r ( i = 0 ; i < SIZE ; i ++){ r = i; c = ( n c o l s ∗ b l o c k s o n r o w ) + myWork ; b u f f e r 2 [ i ] = p i x v a l ( x0 + r ∗ x i n c , y0 + c ∗ y i n c ) ; } MPI Send ( b u f f e r 2 , SIZE , MPI INT , d e s t , t a g + COL TAG, MPI COMM WORLD) ; } } }
6.1
Analisi di complessit` a e scalabilit` a
Indichiamo nuovamente con n = N × N la dimensione della matrice e con s = P × Q il numero degli slave, per un totale di p = s + 1 processi (compreso il master). Sia K il fattore di granularit` a, allora ciascuno slave lavorer`a con sottomatrici di u = K × N/P righe e v = K × N/Q colonne. In totale, la matrice verr` a suddivisa in b = s/K 2 blocchi con eventuali r = N %u righe e c = N %v colonne in eccesso. Sappiamo che il master comunica agli slave l’indice del blocco (ed eventualmente quello della riga e/o colonna) da calcolare, in ogni caso con un messaggio di dimensione 1, pertanto avremo Tcomm1 = (b + r + c)(ts + tw ). Per quanto riguarda la parte computazionale, gli slave calcolano le b sottomatrici ed eventualmente le r righe e le c colonne in eccesso. Ancora una volta, non `e possibile stabilire a priori il numero di iterazioni necessarie a ciascun elemento della matrice, ma possiamo utilizzare max per fornire un upper bound (con tempo corrispondente tmax ) Tcomp ≤ btmax uv + (r + c)tmax N Infine, gli slave inviano la sottomatrice al master utilizzando un messaggio di dimensione u × v. Nel caso in cui r e/o c siano diversi da 0, gli slave inviano anche un messaggio di dimensione N per comunicare al master la riga e/o la colonna in eccesso calcolata. Quanto detto si riassume come segue Tcomm2 = b(ts + tw uv) + (r + c)(ts + tw N ). Ci` o ci permette di arrivare al seguente upper bound del tempo di esecuzione parallelo Tp ≤
(b + r + c)(ts + tw ) + btmax uv + (r + c)tmax N + b(ts + tw uv) + (r + c)(ts + tw N ) p
che, nel caso in cui r = c = 0, pu` o essere scritto come Tp ≤
n b(ts + tw ) + btmax uv + b(ts + tw uv) 2ts + (1 + uv)tw + tmax uv ≤ . =O 2 p K s
Anche in questo caso tw , ts e tmax assumeranno i valori stimati per l’algoritmo con SLB. 20
(7)
Come prima si pu` o fornire un modello di performance pi` u vicino alla realt`a considerando tc anzich`e tmax : a questo proposito, la 7 diventa Tp =
(b + r + c)(ts + tw ) + btc uv + (r + c)tc N + b(ts + tw uv) + (r + c)(ts + tw N ) p
(8)
che nel caso in cui r = c = 0, si pu` o scrivere come Tp =
b(ts + tw ) + btc uv + b(ts + tw uv) tc n + 2bts + (b + n)tw = . p p
(9)
Ricordando che T1 = tc n e sviluppando i calcoli `e possibile determinare anche l’Efficienza che risulta come tc n . (10) Ep = tc n + 2bts + (b + n)tw
6.2
Valutazione e misurazione delle prestazioni
Per questo algoritmo sono state eseguite diverse serie di prove variando, rispettivamente, la griglia di processi slave (ovvero i parametri P e Q) e il parametro di granularit`a K e lasciando costante la dimensione del problema a 1000 × 1000. Le Tabelle 3, 4 e 5 riportano, rispettivamente, i dati raccolti circa il tempo di esecuzione, lo Speedup e l’Efficienza mentre le Figure 9 e 10 riportano, in ordine, i grafici per diversi valori di K, dello Speedup e dell’Efficienza. Griglia (P × Q) seriale 1×2 2×1 2×3 3×2 2×4 4×2
Processi (P × Q + 1) 1 3 3 7 7 9 9
K = 0.1 2.787420 1.352450 1.366970 0.496312 0.491932 0.400846 0.396855
K = 0.25 2.787420 1.363770 1.351950 0.558032 0.537019 0.460090 0.476236
K = 0.50 2.787420 1.468190 1.395150 0.785303 0.813115 0.660600 0.716385
K = 0.75 2.787420 1.808380 2.238810 1.454940 0.989390 1.414530 1.057900
K=1 2.787420 1.873720 1.398500 1.091250 1.668930 0.939970 1.021670
Tabella 3: Tempi di esecuzione dell’algoritmo con DLB per diversi valori di K Griglia (P × Q) seriale 1×2 2×1 2×3 3×2 2×4 4×2
Processi (P × Q + 1) 1 3 3 7 7 9 9
K = 0.1 1 2.06 2.04 5.62 5.67 6.95 7.02
K = 0.25 1 2.04 2.06 5 5.19 6.06 5.85
K = 0.50 1 1.9 2 3.55 3.43 4.22 3.89
K = 0.75 1 1.54 1.25 1.92 2.82 1.97 2.63
K=1 1 1.49 1.99 2.55 1.67 2.97 2.73
Tabella 4: Speedup dell’algoritmo con DLB per diversi valori di K E’ importante notare come, in corrispondenza del valore pi` u basso del coefficiente di granularit`a (ovvero per K = 0.1), si raggiunga lo Speedup di 7.02 per 9 processi, il quale risulta essere il valore pi` u alto ottenuto. Quanto detto ci suggerisce come la granularit`a della decomposizione ricopra un ruolo chiave in questo particolare problema. Infatti gli eventuali sbilanciamenti di carico dovuti ad un partizionamento in grossi blocchi della matrice congiunto ad un SLB vengono superati affinando le suddivisioni e facendo in modo che, anche se un processore `e occupato in un calcolo pi` u lungo, gli altri possano sfruttare il tempo per fare altri calcoli.
21
Griglia (P × Q) seriale 1×2 2×1 2×3 3×2 2×4 4×2
Processi (P × Q + 1) 1 3 3 7 7 9 9
K = 0.1 1 0.69 0.68 0.8 0.81 0.77 0.78
K = 0.25 1 0.68 0.69 0.71 0.74 0.67 0.65
K = 0.50 1 0.63 0.67 0.51 0.49 0.47 0.43
K = 0.75 1 0.51 0.42 0.27 0.4 0.22 0.29
Tabella 5: Efficienza dell’algoritmo con DLB per diversi valori di K
Figura 9: Grafico dello Speedup dell’algoritmo con DLB per diversi valori di K
22
K=1 1 0.5 0.66 0.36 0.24 0.33 0.3
Figura 10: Grafico dell’Efficienza dell’algoritmo con DLB per diversi valori di K Occorre notare che il miglioramento in termini di bilanciamento del carico ottenibile abbassando ulteriormente il valore di K `e limitato dall’overhead di comunicazione che si introdurrebbe avendo una eccessiva quantit` a di blocchi. In particolare, nella Tabella 4 si pu` o vedere come diminuendo K lo Speedup aumenti per tutte le griglie di processi ma in particolare per le griglie di 8 slave: in effetti pi` u slave si hanno pi` u sono i benefici nell’avere una situazione in cui tutti stanno lavorando. Un comportamento analogo `e assunto anche dall’Efficienza. La superiorit` a dell’algoritmo con DLB risulterebbe ancora pi` u evidente se eseguito su un cluster eterogeneo, dove effettivamente ci sono macchine che lavorano a velocit`a diversa e l’algoritmo con SLB sarebbe estremamemte inefficiente. Tale tipo di test non `e stato possibile a causa della mancanza di un siffatto ambiente di testing: ChemGrid consta di nodi tutti tra loro omogenei e la stessa cosa vale per ClusterStudenti. La Figura 11 mostra il confronto tra lo Speedup nel caso statico (senza RMA) e quello nel caso dinamico (con K = 0.1): l’algoritmo dinamico scala molto meglio con il numero dei processi, al contrario di quello statico, il quale mostra, oltre ad un minor Speedup complessivo, anche una minor Scalabilit`a, nel senso che l’aggiunta di altri processi per aiutare nella computazione non comporta molto beneficio.
23
Figura 11: Confronto tra lo Speedup nel caso SLB e nel caso DLB con K=0.1
24
7
Conclusioni
In questo progetto sono state investigate diverse alternative circa la parallelizzazione della computazione dell’insieme di Mandelbrot. Sperimentalmente abbiamo constatato che l’algoritmo con DLB consente un gran miglioramento dello Speedup, passando da un valore di 3.09 nel caso di SLB a 7.02, in entrambi i casi per 9 processi. Per quanto riguarda l’algoritmo con SLB, l’utilizzo di una topologia virtuale cartesiana che raccoglie i processi slave, consente di evitare la comunicazione necessaria alla ditribuzione del lavoro e, quindi, avere un miglioramento delle prestazioni. Tuttavia, l’SLB non si rivela particolarmente efficiente a causa del numero ridotto di slave e delle caratteristiche intrinseche del problema: la natura del frattale ` e tale da impedire di predirre quanto lavoro sar` a necessario per ciascun pixel. Aggiungendo l’RMA all’algoritmo con SLB non si `e avuto un miglioramento delle prestazioni ma, anzi, un lieve peggioramento in quanto l’accesso remoto alla memoria fornisce un beneficio solo quando vengono eseguiti molti trasferimenti di dati di piccole dimensioni tra una sincronizzazione e l’altra: cosa che non avviene nel nostro algoritmo. L’RMA consente, per`o, di semplificare il codice, soprattutto per quanto riguarda il lavoro del master. L’algoritmo con DLB, il quale realizza un task farm dinamico, ci ha consentito di indagare circa l’effetto delle dimensioni dell’unit` a di lavoro. In dettaglio, si `e visto che una dimensione dei blocchi di lavoro pi` u piccola (ovvero K pi` u vicino a 0) consente le prestazioni migliori, in relazione al numero di processi utilizzati. Se il numero di processi `e grande, allora un K basso consente a molti processi, che altrimenti sarebbero stati in idle, di continuare a lavorare, favorendo un aumento dello Speedup e dell’Efficienza. Per quanto riguarda l’algoritmo con DLB nel caso K = 1, esso fornisce una decomposizone della matrice di lavoro del tutto equivalente ai due casi con SLB, tuttavia le prestazioni in questo caso sono peggiori in quanto `e prevista la comunicazione necessaria alla distribuzione dei lavori agli slave, cosa che non avviene nel caso con SLB. Siccome tutti gli algoritmi sviluppati adottano il paradigma master/slave, si `e notato come il numero di slave ricopra un ruolo importante. In tutti e tre i casi, infatti, il processo master rimane in idle per la maggior parte del tempo, occupandosi di allocare lavoro (solo nell’algoritmo con DLB) e ricevere i risultati. Aumentando il numero di slave, aumenta anche il rapporto tra nodi occupati e disoccupati, il quale migliora anche l’Efficienza. Un altro fattore che abbiamo analizzato `e l’impatto delle dimensioni della griglia di slave a parit` a del numero di processi: ovviamente variando P e Q variano anche le dimensioni dei blocchi che vengono assegnati ai processi. In questo caso, l’impatto di P e Q risulta essere pi` u marcato nei due algoritmi con SLB dove essi incidono direttamente sul bilanciamento del carico. Tale influenza permane anche nell’algoritmo con DLB dove, per` o, interviene anche il fattore di granularit`a K. Come ultima osservazione, ricordiamo che le griglie per cui si hanno delle righe e/o delle colonne in eccesso inducono, in generale, un maggior tempo di esecuzione, a parit`a di numero totale di processi, specialmente nei due algoritmi con SLB.
25
Bibliografia e Sitografia [1] W. D. Gropp e R. Thakur, Revealing the Performance of MPI RMA Implementations. Mathematics and Computer Science Division, Argonne National Laboratory, USA. Scaricabile all’indirizzo www.mcs.anl.gov/vthakur/papers/rma-perf.pdf. [2] I. Foster, Designing and building parallel programs. Addison-Wesley Publishing Company. [3] P. Pacheco, A user’s guide to MPI. Department of Mathematics, University of San Francisco, USA. Scaricabile all’indirizzo ftp://math.usfca.edu/pub/MPI/mpi.guide.ps. [4] L. Pacifici, Dispense del corso di Applicazioni e Calcolo in Rete - Corso Avanzato. Corso di Laurea Specialisitca in Informatica, Universit`a degli Studi di Perugia, Italia.
26