Algoritmi paralleli per la moltiplicazione di matrici - Digilander

UNIVERSITA’ CA’ FOSCARI – VENEZIA
Facoltà di Scienze Matematiche, Fisiche e
Naturali
Corso di Laurea in Informatica
Progetto di Calcolo Parallelo
Prof. S. Orlando
Algoritmi paralleli per
la moltiplicazione di
matrici
Anno Accademico 2006-2007
Franzin Alberto
Savian Dario
Marchetto Giovanni
Mat. 807980
Mat. 808548
Mat. 810134
INTRODUZIONE............................................................................................................................................................. 4
IMPLEMENTAZIONE ................................................................................................................................................... 5
MATRIX-MATRIX MULTIPLICATION ............................................................................................................................... 5
Funzionamento.......................................................................................................................................................... 5
[master]
Lettura e partizionamento matrici A e B .............................................................................................. 6
[master]
Spedizione sottoblocchi ai processi....................................................................................................... 7
[master e slave] Ricezione dei sottoblocchi .......................................................................................................... 8
[master e slave] Scambio blocchi mancanti tra processi ...................................................................................... 9
[master e slave] Moltiplicazione sottoblocchi (calcolo sottoblocco della matrice C) ........................................ 10
[master e slave] Spedizione blocco calcolato al master...................................................................................... 12
[master]
Raccolta di tutti i blocchi e creazione della matrice C ....................................................................... 13
CANNON ALGORITHM ................................................................................................................................................... 14
Funzionamento........................................................................................................................................................ 14
[master]
Lettura e partizionamento matrici A e B ............................................................................................ 17
[master]
Allineamento delle matrici A e B......................................................................................................... 17
[master]
Spedizione sottoblocchi ai processi..................................................................................................... 19
[master e slave] Ricezione dei sottoblocchi ........................................................................................................ 20
[master e slave] Rotazione sottoblocchi e calcolo blocco della matrice C ......................................................... 20
[master e slave] Spedizione blocco calcolato al master...................................................................................... 25
[master]
Raccolta di tutti i blocchi e creazione della matrice C ....................................................................... 26
ALGORITMI SEQUENZIALI PER LA MOLTIPLICAZIONE DELLE MATRICI............................................. 34
ALGORITMO NORMALMENTE UTILIZZATO ..................................................................................................................... 34
Algoritmo di Strassen .............................................................................................................................................. 34
TEST PROGETTO ........................................................................................................................................................ 36
PRINCIPALI GRAFICI REALIZZATI CON RELATIVI COMMENTI.......................................................................................... 36
COMMENTO GRAFICI NUMERO PROCESSI VS. TEMPO .................................................................................................... 37
Matrice 384 ............................................................................................................................................................. 37
. Matrice 1920 ......................................................................................................................................................... 38
COMMENTO GRAFICI RITARDO VS. TEMPO................................................................................................................... 41
Numero processi 4................................................................................................................................................... 41
Numero processi 16................................................................................................................................................. 43
GRAFICI DIMENSIONE MATRICE – TEMPO ...................................................................................................................... 43
Funzione di rallentamento disattivata..................................................................................................................... 43
Funzione di rallentamento attivata ......................................................................................................................... 45
GRAFICI DIMENSIONE NUMERO PROCESSORI – SPEEDUP................................................................................................ 46
Matrice 384 ............................................................................................................................................................. 46
Matrice 1152 ........................................................................................................................................................... 48
MATRICE 1920.............................................................................................................................................................. 49
GRAFICI DIMENSIONE NUMERO PROCESSORI – EFFICIENZA ........................................................................................... 51
Matrice 384 ............................................................................................................................................................. 52
Matrice 1152 ........................................................................................................................................................... 53
MATRICE 1190.............................................................................................................................................................. 54
VALUTAZIONE BILANCIAMENTO ................................................................................................................................... 57
Matrice 384 ............................................................................................................................................................. 57
Matrice 1920 ........................................................................................................................................................... 60
CARATTERISTICHE DEL SISTEMA ....................................................................................................................... 64
ELENCO FILE PROGETTO ............................................................................................................................................... 64
RAPPRESENTAZIONE MATRICE ...................................................................................................................................... 66
FUNZIONI DI UTILITÀ .................................................................................................................................................... 66
IMPLEMENTAZIONE DELLA FUNZIONE F DI RALLENTAMENTO ....................................................................................... 68
MANUALE D’USO ........................................................................................................................................................ 69
COMPILAZIONE ATTRAVERSO MAKEFILE ...................................................................................................................... 69
MODIFICA HEADER DEI FILE .......................................................................................................................................... 69
UTILIZZO ...................................................................................................................................................................... 70
DOCUMENTAZIONE DELLE MISURE DELLE PRESTAZIONI......................................................................... 71
2
PRIMITIVE MPI............................................................................................................................................................ 73
PRIMITIVE GESTIONE AMBIENTE MPI ........................................................................................................................... 73
MPI_Init( ) .............................................................................................................................................................. 73
MPI_Comm_size ..................................................................................................................................................... 73
MPI_Comm_rank.................................................................................................................................................... 73
MPI_Abort .............................................................................................................................................................. 73
MPI_Get_Processor_Name .................................................................................................................................... 73
MPI_Wtime ............................................................................................................................................................. 74
MPI_Finalize .......................................................................................................................................................... 74
PRIMITIVE COMUNICAZIONE POINT TO POINT ............................................................................................................... 74
MPI_Send( ) ............................................................................................................................................................ 74
MPI_Recv( ) ............................................................................................................................................................ 74
MPI_Sendrecv_replace ........................................................................................................................................... 75
COLLETTIVE DI COMUNICAZIONE.................................................................................................................................. 75
MPI_Barrier............................................................................................................................................................ 75
MPI_Bcast............................................................................................................................................................... 75
MPI_Scatter ............................................................................................................................................................ 76
MPI_Gather ............................................................................................................................................................ 76
MPI_Allgather ........................................................................................................................................................ 76
PRIMITIVE PER LA GESTIONE DEI COMUNICATORI ......................................................................................................... 77
MPI_Comm_Split.................................................................................................................................................... 77
TIPI PREDEFINITI IN MPI............................................................................................................................................... 77
RISULTATI TEST ......................................................................................................................................................... 78
ALGORITMO MATRIX MATRIX ...................................................................................................................................... 78
ALGORITMO CANNON ................................................................................................................................................... 79
ALGORITMO PROCESSOR FARM .................................................................................................................................... 81
ALGORITMO SEQUENZIALE ........................................................................................................................................... 82
CONCLUSIONE ............................................................................................................................................................ 83
APPENDICE ................................................................................................................................................................... 84
GRAFICI NUMERO PROCESSORI – TEMPO ....................................................................................................................... 84
Matrice 384 ............................................................................................................................................................. 84
Matrice 768 ............................................................................................................................................................. 86
Matrice 1152 ........................................................................................................................................................... 88
Matrice 1920 ........................................................................................................................................................... 90
GRAFICI RITARDO – TEMPO ........................................................................................................................................... 91
Matrice 384 ............................................................................................................................................................. 91
Matrice 768 ............................................................................................................................................................. 93
Matrice 1152 ........................................................................................................................................................... 94
Matrice 1920 ........................................................................................................................................................... 95
GRAFICI DIMENSIONE MATRICE – TEMPO ...................................................................................................................... 97
Funzione di rallentamento disattivata..................................................................................................................... 97
Funzione di rallentamento attivata : 5 cicli ............................................................................................................ 98
Funzione di rallentamento attivata : 10 cicli .......................................................................................................... 99
Funzione di rallentamento attivata : 40 cicli ........................................................................................................ 100
GRAFICI NUMERO PROCESSI – SPEEDUP....................................................................................................................... 101
Matrice 384 ........................................................................................................................................................... 101
Matrice 768 ........................................................................................................................................................... 103
Matrice 1152 ......................................................................................................................................................... 104
Matrice 1920 ......................................................................................................................................................... 105
GRAFICI NUMERO PROCESSORI – EFFICIENZA .............................................................................................................. 107
Matrice 384 ........................................................................................................................................................... 107
Matrice 768 ........................................................................................................................................................... 109
Matrice 1152 ......................................................................................................................................................... 110
Matrice 1920 ......................................................................................................................................................... 112
GRAFICI BILANCIAMENTO DEL CARICO ....................................................................................................................... 114
Matrice 384 ........................................................................................................................................................... 114
Matrice 1920 ......................................................................................................................................................... 115
3
Introduzione
Il progetto consiste nell’implementazione di tre algoritmi paralleli per la moltiplicazione delle
matrici.
Gli algoritmi implementati differiscono in base al processo di parallelizzazione realizzato:
Matrix-Matrix Multiplication: assegnazione statica del job ai processi; sono necessari
meccanismi di comunicazione per fornire il sottoblocco posseduto a tutti i processi che
hanno un blocco che è nella stessa riga o colonna di quello posseduto.
Cannon Algorithm: assegnazione statica del job ai processi; prima di applicare l’algoritmo
bisogna allineare le matrici. Lo scambio dei sottoblocchi tra processi avviene tramite una
rotazione dei sottoblocchi posseduti dai processi.
Processor Farm: mappatura dinamica dei job ai processi; l’assegnazione dei blocchi è
eseguita dal master e non si ha nessuna iterazione tra i worker.
Il progetto è stato implementato tramite lo standard MPI (Massage Passing Interface). MPI è lo
standard de facto per la comunicazione tra processi in ambiente distribuito tra nodi appartenenti ad
un cluster, che eseguono il programma parallelo secondo il modello SIMD (Single Istruction
Multiple Data). Tale libreria ha il vantaggio di essere portabile poiché esistono implementazioni per
diverse architetture e veloce, infatti è ottimizzata in base all’architettura presente.
L’implementazione più diffusa di MPI, che è quella che è stata adotta nel progetto, è MPICH: opensource, testata in svariate piattaforme (incluse Linux (on IA32 and x86-64), Mac OS/X (PowerPC
and Intel), Solaris (32- and 64-bit), e Windows).
Requisiti del progetto:
Le matrici di input devono essere accedute solamente dal processo 0
Le matrici sono di tipo FP e sono memorizzate nel formato row-major come ASCII file nel
disco
Nell’algoritmo 1 e 2, i blocchi devono essere trasmessi dal processo 0 che ha il compito di
recuperare i blocchi calcolati dai worker
Nell’algoritmo 3, il master è responsabile della distribuzione dei dati a tutti gli slave
La matrice di output deve essere “ricomposta” dal master che provvederà poi a salvarla sul
disco
Deve essere possibile disabilitare l’input dei dati dal/al disco
Obiettivi:
Valutare l’eterogeneità e lo sbilanciamento del cluster
Valutare lo speedup con differenti granularità e differenti dimensioni del problema
Siccome la moltiplicazione delle matrici non è sufficiente per ottenere un vantaggio nell’utilizzo
degli algoritmi paralleli, computare C = f(A) × B invece che C = A × B. La funzione f deve essere
indipendentemente applicata a tutti gli elementi A[i,j] per permettere l’incremento della granularità
minima dei task.
4
Implementazione
Viene ora spiegato il funzionamento degli algoritmi e l’implementazione che è stata realizzata.
Matrix-Matrix Multiplication
Funzionamento
Consideriamo la moltiplicazione di due matrici quadrate di dimensione n × n : C = A × B.
Il primo passo eseguito dall’algoritmo è quello di partizionare le matrici A e B in p sottoblocchi,
dove p è il numero dei processi che eseguono l’algoritmo. Ogni sottoblocco Ai,j della matrice A e
Bi,j della matrice B (0≤i, j<p) ha dimensione (n/ p ) × (n/ p ) ciascuno: tale blocco è assegnato
dal master al processo Pi,j.
A questo punto, il processo Pi,j per essere in grado di calcolare il blocco Ci,j della matrice C, deve
perciò possedere tutti i sottoblocchi Ai,k e Bk,j (0≤k< p ), cioè tutti i sottoblocchi che appartengono
alla stessa riga del sottoblocco posseduto della matrice A e di tutti i sottoblocchi che appartengono
alla stessa colonna del sottoblocco posseduto della matrice B.
Per fare ciò, i processi si scambiano i sottoblocchi mancanti per poter eseguire la moltiplicazione:
I sottoblocchi mancanti della riga per la matrice A
I sottoblocchi mancanti della colonna per la matrice B
Ogni processo a questo punto possiede tutti gli elementi necessari per il calcolo del blocco Cij di
competenza al processo.
Terminata la computazione del sottoblocco, ogni processo Pi,j invia tale blocco al processo master
che ha il compito di assemblare i blocchi ricevuti ed ottenere la matrice C.
In base alla dimensione delle matrici A e B, poiché i sottoblocchi devono essere quadrati, non è
possibile eseguire l’algoritmo con un numero casuale di processi.
Per chiarire tale limitazione, viene fornito un esempio con una matrice di dimensione 48. Il numero
dei processi che sono congrui con tale dimensione sono:
1 processo
4 processi
9 processi
16 processi
……
1 sottoblocco di dimensione 48
4 sottoblocchi di dimensione 24
9 sottoblocchi di dimensione 16
16 sottoblocchi di dimensione 12
5
Riassumendo:
[master]
[master]
[master]
[master e slave]
[master e slave]
[master e slave]
[master e slave]
[master]
Lettura matrice A e B
Partizionamento delle matrici A e B
Spedizione sottoblocchi ai processi
Ricezione dei sottoblocchi
Scambio blocchi mancanti tra processi
Moltiplicazione sottoblocchi (calcolo sottoblocco della matrice C)
Spedizione blocco calcolato al master
Raccolta di tutti i blocchi e creazione della matrice C
Graficamente, le operazioni eseguite da un certo processo sono:
[master]
Lettura e partizionamento matrici A e B
Il master è l’unico che può accedere alle matrici A e B. La lettura di tali matrici avviene da disco se
è settata la variabile FROM_FILE altrimenti vengono generate automaticamente della dimensione
specificata in DIM.
Viene controllato che se sia la matrice A che la matrice B possano essere suddivise in sottoblocchi
quadrati, in base alla loro dimensione e al numero dei processi sui quali si intende eseguire
l’algoritmo. Tale compito è eseguito dalla funzione computabile.
Si procede inoltre all’allocazione delle spazio necessario a contenere a matrice C e delle strutture
dati necessarie all’esecuzione dell’algoritmo.
A questo punto si deve procedere al partizionamento delle matrici. Sono state, a tale scopo,
realizzate le funzioni getCoordX e getCoordY; la prima data la dimensione della matrice e il rank
del processo restituisce la coordinata dell’asse X dell’angolo più in alto a sinistra del sottoblocco
della matrice assegnata al processo; la seconda calcola la coordinata Y.
6
Viene chiarito tutto ciò con un esempio:
Supponiamo di voler eseguire l’algoritmo con 4 processi e di avere la seguente matrice A:
1
5
9
13
2
6
10
14
3
7
11
15
4
8
12
16
Quello che si vorrebbe ottenere è il seguente partizionamento:
1
5
9
13
2
6
10
14
3
7
11
15
4
8
12
16
Rank 0
Rank 1
Rank 2
Rank 3
Per ottenere ciò, le funzioni getCoordX e getCoordY restituiscono le seguenti coordinate:
Rank processo
0
1
2
3
[master]
Coordinata X
0
0
2
2
Coordinata Y
0
2
0
2
Spedizione sottoblocchi ai processi
Conosciute le coordinate assegnate al processo, il passo seguente è quello di creare un array
contenente tutti i dati che devono essere spediti. Per prima cosa si inseriscono nell’array gli
elementi del sottoblocco assegnato al processo 0, poi gli elementi del sottoblocco assegnati al
processo 1 e così via; tale funzionalità è implementata dalla funzione mat2Array.
1
2
5
6
3
4
7
8
9
10
13
14
11
12
15
16
Per effettuare la distribuzione dei blocchi, siccome siamo in presenza di un contesto distribuito, è
necessaria per prima cosa comunicare a tutti gli slave quale è la dimensione del sottoblocco che gli
viene assegnato tramite la funzione MPI_BCast():
// area di ogni sottoblocco della matrice assegnato ad un processo
b=(int)(n/num_proc);
//distribuzione della dimensione del sottoblocco
MPI_Bcast(&b,1,MPI_INT,0,MPI_COMM_WORLD);
7
Per la distribuzione vera e propria dei sottoblocchi è stata utilizzata un’operazione di MPI_Scatter():
tale funzione prende in ingresso l’array, lo divide e lo distribuisce automaticamente a tutti i processi
(compreso il master stesso), garantendo l’ordinamento rispetto al numero di rank.
//conversione matrice A in array
arrA=(double *)mat2Array(matA,num_proc);
//conversione matrice B in array
arrB=(double *)mat2Array(matB,num_proc);
//scatter dei sottobolcchi di A ai processi
MPI_Scatter(arrA,b,MPI_DOUBLE,subArrA,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
//scatter dei sottobolcchi di B ai processi
MPI_Scatter(arrB,b,MPI_DOUBLE,subArrB,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
1
2
5
Rank 0
6
3
4
7
8
9
Rank 1
10
13
14
11
Rank 2
12
15
16
Rank 3
[master e slave] Ricezione dei sottoblocchi
Tutti gli slave e il master, invocando la funzione MPI_Scatter, memorizzano il sottoblocco ricevuto
in un array di double (subArrA per matrice A e subArrB per matrice B).
// allocazione spazio per i sottobloblocchi della matrice A e B
subArrA=(double*)calloc((int)b,sizeof(double));
subArrB=(double*)calloc((int)b,sizeof(double));
//ricezione del sottoblocco di A e B
MPI_Scatter(arrA,b,MPI_DOUBLE,subArrA,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
MPI_Scatter(arrB,b,MPI_DOUBLE,subArrB,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
8
Rank 0
possiede
1
2
5
6
Rank 1
possiede
3
4
7
8
Rank 2
possiede
9
10
13
14
Rank 3
possiede
11
12
15
16
[master e slave] Scambio blocchi mancanti tra processi
Tutti i processi hanno così ottenuto il proprio sottoblocco della matrice; ora è necessaria la fase di
scambio dei sottoblocchi (ricevuti sotto forma di array) tra processi.
A questo proposito, ogni processo determina, sempre tramite le funzioni getCoorX e getCoordY, le
coordinate dell’asse delle X e Y dell’angolo più in alto a sinistra del sottoblocco assegnatoli:
myCoordX e myCoordY.
Quindi ogni processo crea un comunicatore MyComm_row contenente tutti e solo i processi che
hanno la stessa coordinata X e MyComm_col la stessa coordinata Y.
MyComm_row sarà utilizzato per lo scambio dei sottoblocchi tra processi che hanno il blocco
ricevuto nella stessa riga della matrice A mentre MyComm_col per lo scambio dei sottoblocchi tra
processi che hanno il blocco ricevuto nella stessa colonna della matrice B.
Abbiamo la seguente situazione:
Rank Processo
0
1
2
3
MyComm_row
0,1
0,1
2,3
2,3
MyComm_col
0,2
1,3
0,2
1,3
Infatti, ad esempio, il processo 0 e il processo 1 hanno il sottoblocco della matrice A che è nella
stessa riga e perciò hanno lo stesso comunicatore MyComm_row.
// coordinata x e y del processo
myCoordX= getCoordX(rank,lato_mat,(int)sqrt(b));
myCoordY= getCoordY(rank,lato_mat,(int)sqrt(b));
//creazione di nuovi comunicatori
MPI_Comm_split(MPI_COMM_WORLD,myCoordX,rank,&MyComm_row);
MPI_Comm_split(MPI_COMM_WORLD,myCoordY,rank,&MyComm_col);
9
Per la comunicazione dei sottoblocchi tra processi è stata utilizzata la funzione MPI_Allgather;
tramite l’utilizzo di tale funzione ogni processo memorizza due array :
rowA contiene tutti i sottoblocchi della stessa riga della matrice A del blocco associato
processo
colB possiede tutti i sottoblocchi della stessa colonna della matrice B del blocco associato
processo
Ad esempio, l’array rowA (degli elementi della matrice A) posseduto dal processo 0 e dal processo
1 sono uguali mentre sono identici l’array colB del processo 0 e 2.
Processo 0:
rowA
1
2
5
6
3
4
7
8
colB
1
2
5
6
9
10
13
14
rowA
1
2
5
6
3
4
7
8
colB
3
4
7
8
11
12
15
16
Processo 1:
//array di tutte le righe ricevute
MPI_Allgather(subArrA,b,MPI_DOUBLE,rowA,b,MPI_DOUBLE,MyComm_row);
//array di tutte le colonne ricevute
MPI_Allgather(subArrB,b,MPI_DOUBLE,colB,b,MPI_DOUBLE,MyComm_col);
A questo punto ogni processo possiede la riga della matrice A e la colonna della matrice B: è quindi
in grado di calcolare il sottoblocco della matrice C di sua competenza.
[master e slave] Moltiplicazione
matrice C)
sottoblocchi
(calcolo
sottoblocco
della
L’ultimo passo prima della moltiplicazione è trasformare l’ array rowA e colB nella forma di
un’array di sottomatrici, denominati rispettivamente riga e colonna
10
rowA
1
2
5
1
5
riga
colB
6
1
2
5
riga
3
2
6
6
1
5
4
3
7
9
2
6
8
13
14
4
8
10
9
13
7
10
14
Adesso è possibile effettuare la moltiplicazione tra l’array riga e colonna ottenendo la matrice calC :
il blocco della matrice C che il processo dove calcolare.
matrice *calC, *ris_temp;
for(i=0;i<sqrt(num_proc);i++){
ris_temp=ProdottoMat(riga[i],colonna[i],((int)sqrt(b)));
if(i==0)
calC=ris_temp;
else
SommaMat(calC,ris_temp);
}
Il codice della funzione ProdottoMat è riportato qui sotto. Si può notare che se non è settata SLOW
non viene applicata la funzione di ritardo. Quando invece tale variabile è settata, la variabile
RITARDO specifica il numero di cicli da eseguire; variando tale valore è possibile verificare il
comportamento dell’algoritmo con diversi livelli di granularità dei task.
11
// calcolo del prodotto tra due matrici
matrice* ProdottoMat(matrice * a, matrice* b, int d){
double somma=0.0;
int i,j,h;
matrice *calC=(matrice*)malloc(sizeof(matrice));
calC->dim = d;
calC->val = allocaMatrice(d);
for(i=0; i<d;i++){
for(j=0;j<d; j++){
somma=0.0;
for(h=0;h<d;h++){
if(SLOW){
int g, s=0;
for(g=0;g<RITARDO;g++)
s++;
}
somma=somma+(a->val[i][h]*b->val[h][j]);
}
calC->val[i][j]= (double) somma;
}
}
return calC;
}
[master e slave] Spedizione blocco calcolato al master
Ogni processo possiede il sottoblocco da spedire al master e per prima cosa lo deve convertire in
array per la spedizione.
double *subArrC;
// conversione del blocco della matrice C calcolata dal processo
subArrC=subMat2subArray(calC);
77
79
78
80
77
78
79
80
subArrC
Attraverso la funzione MPI_Gather ogni processo spedisce il proprio array al master.
// spedizione
sottoblocchi
del
sottoblocco
calcolato
della
matrice
C
e
ricezione
dei
MPI_Gather(subArrC,b,MPI_DOUBLE,arrC,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
12
[master]
Raccolta di tutti i blocchi e creazione della matrice C
Il master tramite l’invocazione della funzione MPI_Gather raccoglie tutti i sottoblocchi (spediti
sottoforma di array) e li memorizza nell’array arrC, ordinati per numero del rank dei processi
mittenti (prima quelli del processo 0, poi quelli del processo 1 e così via).
MPI_Gather(subArrC,b,MPI_DOUBLE,arrC,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
Rank 0
22
23
24
25
Rank 1
26
27
28
29
Rank 2
30
31
32
33
Rank 3
34
34
36
37
arrC
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Ottenuto l’array rimane da convertirlo in matrice: la matrice finale C = A × B. Tale conversione
viene realizzata dalla funzione array2MatFormatted.
// conversione dell'array in matrice
matC=array2MatFormatted(arrC,matA->dim,num_proc);
arrC
22
23
24
25
26
27
28
29
30
22
24
30
32
23
25
31
33
26
28
34
36
27
29
35
37
31
32
33
34
35
36
37
13
Cannon Algorithm
Funzionamento
Consideriamo ancora la moltiplicazione di due matrici quadrate di dimensione n, C = A × B.
Anche in questo algoritmo, come nel precedente, le matrici A e B vengono partizionate in p
sottoblocchi quadrati, dove p è il numero di processi sul quale si intende eseguire l’algoritmo.
I processi vengono etichettati da P0,0 a P p -1,
Ai,j e Bi,j al processo Pi,j.
p -1 ed inizialmente viene associato il sottoblocco
Ogni processo della riga i-esima richiede tutte le
p sottomatrici Ai,k (0≤k< p ); è possibile
schedulare la computazione dei p processi nell’ i-esima riga in moda tale che, in un certo istante
di tempo, ogni processo usa un differente sottoblocco Ai,k. Questi blocchi possono così essere
ruotati sistematicamente tra i processi dopo ogni moltiplicazione delle sottomatrici così che ogni
processo possiede in ogni rotazione un nuovo sottoblocco Ai,k.
Se una identica schedulazione è applicata per le colonne nessun processo necessita di più di un
sottoblocco di ogni matrice alla volta , ottenendo un risparmio della memoria necessaria per la
computazione.
Il passo iniziale consiste nell’allineamento delle matrici A e B in modo che ogni processo possa
moltiplicare i propri sottoblocchi posseduti in maniera indipendente. A tale scopo viene applicato
uno shift a sinistra (con wraparound) di i step a tutti i sottoblocchi Ai,j della matrice A. Inoltre, tutti i
sottoblocchi Bi,j della matrice B sono shiftati in alto di j step (con wraparound).
Tali operazioni fanno si che il processo Pi,j abbia associato il sottoblocco Ai,(j+i)mod p e
B(i+j)mod p ,j.
A questo punto ogni processo è pronto per eseguire la prima moltiplicazione tra le sottomatrici.
Dopo aver eseguito la moltiplicazione, ogni blocco della matrice A si “muove” di uno step a sinistra
e ogni blocco della matrice B si muove di uno step in alto (ancora con wraparound).
Ogni processo esegue la moltiplicazione dei sottoblocchi ricevuti, somma il risultato a quello
ottenuto nel passo precedente e si ripete tale meccanismo fino a che tutti i processi non hanno
eseguito tutte p moltiplicazioni.
Terminata la rotazione, ogni processo Pi,j invia il blocco calcolato della matrice Ci,j al processo
master che ha il compito di assemblare i blocchi ricevuti ed ottenere la matrice C.
Come per l’algoritmo precedente, in base alla dimensione delle matrici A e B, poiché i sottoblocchi
devono essere quadrati, non è possibile eseguire l’algoritmo con un numero casuale di processi.
14
Riassumendo:
[master]
[master]
[master]
[master]
[master e salve]
[master e slave]
[master e slave]
[master e slave]
[master e slave]
[master e slave]
[master e slave]
[master e slave]
[master e slave]
[master]
Lettura delle matrici A e B
Partizionamento delle matrici A e B
Allineamento delle matrici A e B
Spedizione sottoblocchi ai processi
Ricezione dei sottoblocchi
for k=1 to p
Esecuzione prodotto sottoblocchi
Somma risultato con quello ottenuto al passo precedente
Spedizione blocco della matrice A al processo a sinistra
Ricezione blocco della matrice A dal processo a destra
Spedizione blocco della matrice B al processo sopra
Ricezione blocco della matrice B dal processo sotto
Spedizione blocco calcolato al master
Raccolta di tutti i blocchi e creazione della matrice C
Graficamente, l’algoritmo può essere spiegato nel seguente modo:
Allineamento iniziale delle matrici A e B eseguita dal master
15
Matrici A e B dopo l’allineamento eseguito dal master (c) e dopo il primo shift
Matrici A e B dopo il secondo shift (e) e dopo il terzo shift (f)
16
[master]
Lettura e partizionamento matrici A e B
Come per l’algoritmo precedente, il master è l’unico che può accedere alle matrici A e B. La lettura
di tali matrici avviene da disco se è settata la variabile FROM_FILE altrimenti vengono generate
automaticamente della dimensione specificata da DIM.
Viene controllato se sia la matrice A che la matrice B possono essere suddivise in sottoblocchi
quadrati, in base alla loro dimensione e al numero dei processi sui quali si intende eseguire
l’algoritmo. Tale compito è eseguito dalla funzione computabile.
Si procede inoltre all’allocazione delle spazio necessario a contenere la matrice C e delle strutture
dati necessarie all’esecuzione dell’algoritmo.
[master]
Allineamento delle matrici A e B
Prima di consegnare il sottoblocchi della matrice A e B bisogna procedere all’allineamento delle
matrici.
L’allineamento della matrice A è eseguito dalla funzione SwapRow mentre quello della matrice B è
realizzato dalla funzione SwapCol.
// passo iniziale: allinemento della matrice A e B
SwapRow(matA,num_proc);
SwapCol(matB, num_proc);
Consideriamo ad esempio la matrice A partizionata nel seguente modo:
1
5
9
13
2
6
10
14
3
7
11
15
4
8
12
16
Rank 0
Rank 1
Rank 2
Rank 3
Ogni sottoblocco Ai,j viene spostato a sinistra di i posizioni (con wraparound).
I sottoblocchi della prima riga rimangono nella posizione dove sono, quelli della seconda riga si
spostano a sinistra di una posizione.
Quello che si vuole ottenere quindi è la seguente situazione:
1
5
11
15
2
6
12
16
3
7
9
13
4
8
10
14
Rank 0
Rank 1
Rank 2
Rank 3
17
Per ottenere ciò è stata realizzata una funzione che, date le coordinate dell’angolo più in alto a
sinistra del sottoblocco, calcola le nuove coordinate del blocco (siccome avviene uno spostamento a
sinistra la coordinata dell’asse delle x rimane uguale).
Rank processo
0
1
2
3
Coordinata X
0
0
2
2
Coordinata Y
0
2
0
2
Rank processo
0
1
2
3
Coordinata X
0
0
2
2
Coordinata Y
0
2
2
0
A questo punto sono disponibili le nuove coordinate di ogni processo; per eseguire lo spostamento
vero e proprio e realizzare tutto ciò con un utilizzo limitato di memoria, si procede per righe e si
utilizza un array di sottoblocchi temporaneo (utilizzato per contenere i blocchi della riga considerata
nella posizione corretta): viene presa in considerazione la prima riga e vengono ricopiati i
sottoblocchi nell’array in posizione corretta e quando tutti i sottoblocchi sono stati copiati, viene
ricopiato il contenuto dell’array nella prima riga della matrice. Si procede successivamente con la
seconda riga, si ricopiano i sottoblocchi nell’array in posizione corretta e poi si ricopia il contenuto
dell’array nella seconda riga della matrice.
Vediamo l’esempio con la seconda riga, i sottoblocchi devono essere spostati di una posizione a
sinistra.
1
5
9
13
2
6
10
14
3
7
11
15
4
8
12
16
Riga presa in considerazione
I blocchi vengono copiati nell’array temporaneo in posizione corretta:
11
15
12
16
9
13
10
14
1
5
11
15
2
6
12
16
3
7
9
13
4
8
10
14
Il contenuto dell’array viene copiato nella seconda riga della matrice ottenendo il risultato voluto.
18
Un procedimento simile è realizzato per la matrice B ma invece di procedere per righe si procede
per colonne.
[master]
Spedizione sottoblocchi ai processi
Il passo seguente è quello di creare un array contenente tutti i dati che devono essere spediti: la
matrice deve essere convertita in array per la spedizione.
Per prima cosa si inseriscono nell’array gli elementi del sottoblocco assegnato al processo 0, poi gli
elementi del sottoblocco assegnati al processo 1 e così via; tale funzionalità è implementata dalla
funzione mat2Array.
1
2
5
6
3
4
7
8
11
12
15
16
9
10
13
14
Per effettuare la distribuzione dei blocchi è necessaria per prima cosa comunicare a tutti gli slave
quale è la dimensione del sottoblocco che gli viene assegnato tramite la funzione di MPI_BCast():
// area di ogni sottoblocco della matrice assegnato ad un processo
b=(int)(n/num_proc);
//distribuzione della dimensione del sottoblocco
MPI_Bcast(&b,1,MPI_INT,0,MPI_COMM_WORLD);
Per la distribuzione vera e propria dei sottoblocchi è stata utilizzata un’operazione di MPI_Scatter():
tale funzione prende in ingresso l’array, lo divide e lo distribuisce automaticamente a tutti i processi
(compreso il master stesso), garantendo l’ordinamento rispetto al numero di rank.
//conversione matrice A in array
arrA=(double *)mat2Array(matA,num_proc);
//conversione matrice B in array
arrB=(double *)mat2Array(matB,num_proc);
//scatter dei sottobolcchi di A ai processi
MPI_Scatter(arrA,b,MPI_DOUBLE,subArrA,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
//scatter dei sottobolcchi di B ai processi
MPI_Scatter(arrB,b,MPI_DOUBLE,subArrB,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
19
1
2
5
6
3
Rank 0
4
7
8
11
12
Rank 1
15
16
9
Rank 2
10
13
14
Rank 3
[master e slave] Ricezione dei sottoblocchi
Tutti gli slave e il master, invocando la funzione MPI_Scatter, memorizzano il sottoblocco ricevuto
in un array di double (subArrA per la matrice A e subArrB per la matrice B).
// allocazione spazio per i sottobloblocchi della matrice A e B
subArrA=(double*)calloc((int)b,sizeof(double));
subArrB=(double*)calloc((int)b,sizeof(double));
//ricezione del sottoblocco di A e B
MPI_Scatter(arrA,b,MPI_DOUBLE,subArrA,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
MPI_Scatter(arrB,b,MPI_DOUBLE,subArrB,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
Rank 0
possiede
1
2
5
6
Rank 1
possiede
3
4
7
8
Rank 2
possiede
11
12
15
16
Rank 3
possiede
9
10
13
14
[master e slave] Rotazione sottoblocchi e calcolo blocco della matrice C
L’array ricevuto (sia quello della matrice A che B) viene convertito in matrice tramite la funzione
array2Mat.
/* conversione del blocco della matrice A ricevuto da array in matrice
l: lato del sottoblocco
*/
ricA=(matrice*)malloc(sizeof(matrice));
int l=(int)(sqrt(b));
array2Mat(ricA,subArrA,l);
20
// conversione del blocco della matrice B ricevuto da array in matrice
ricB=(matrice*)malloc(sizeof(matrice));
array2Mat(ricB,subArrB,l);
Ad esempio il processo 0 converte subArrA in matrice:
1
2
5
1
5
6
[master e slave]
for k=1 to
1.
[master e slave]
2.
[master e slave]
3.
[master e slave]
4.
[master e slave]
5.
[master e slave]
6.
[master e slave]
2
6
p
Esecuzione prodotto sottoblocchi
Somma risultato con quello ottenuto al passo precedente
Spedizione blocco della matrice A al processo a sinistra
Ricezione blocco della matrice A dal processo a destra
Spedizione blocco della matrice B al processo sopra
Ricezione blocco della matrice B dal processo sotto
Vengono eseguiti p cicli, in ogni ciclo ogni processo esegue la moltiplicazione del sottoblocco
posseduto della matrice A con quello della matrice B; eseguito ciò bisogna sommare il risultato con
quello ottenuto nelle fasi precedenti.
Viene allocata, a tale scopo, la matrice c_final utilizzata per memorizzare i risultati calcolati fino al
passo precedente e c_parz per contenere il prodotto delle matrici eseguito nel ciclo; tale prodotto
deve essere sommato al risultato ottenuto nelle fasi precedenti, il quale è presente nella matrice
c_final. Il risultato di tale somma viene memorizzato nella matrice c_final.
In ogni ciclo quindi:
c_parz = (Ai,j ricevuto) * (Bi,j ricevuto)
c_final = c_final + c_parz
// calcolo del prodotto del sottoblocco della matrice A e B ricevute
c_parz=(matrice*)ProdottoMat(ricA,ricB,l);
// somma del risultato dell'iterazione corrente con quelli ottenuti nelle
iterazioni precedenti
c_final=(matrice*)SommaMat2(c_parz,c_final);
Come per l’algoritmo precedente, dato che viene utilizzata la stessa funzione di moltiplicazione
delle matrici, se non è settata SLOW non viene applicata la funzione di ritardo. Quando invece tale
variabile è settata, la variabile RITARDO specifica il numero di cicli da eseguire; variando tale
valore è possibile verificare il comportamento dell’algoritmo con diversi livelli di granularità dei
task.
21
Per eseguire lo scambio dei blocchi della matrice A, ogni processo deve spedire il sottoblocco della
matrice A al processo a sinistra e deve ricevere il nuovo blocco dal processo a destra.
Per fare ciò sono state implementate due funzioni:
getRankRowDest determina il rank del processo a cui devo inviare il sottoblocco della
matrice A
getRankRowMit specifica il rank del processo da cui devo ricevere il sottoblocco della
matrice A
// determinazione del rank del processo a cui devo inviare il sottoblocco di A
posseduto
row_dest=getRankRowDest(rank,num_proc);
// determinazione del rank del processo da cui devo ricevere il sottoblocco di A
row_mit=getRankRowMit(rank,num_proc);
Ad esempio, consideriamo una esecuzione con 16 processi:
1
5
9
13
2
6
10
14
3
7
11
14
4
8
12
16
Rank processo
1
2
3
4
Da chi devo ricevere
2
3
4
1
A chi devo spedire
4
1
2
3
Il processo a cui inviare il blocco è quello a sinistra tranne il caso particolare in cui il blocco si trova
nella prima posizione della riga: il processo destinatario è l’ultimo della riga.
Nell’esempio, il processo 1 deve inviare il blocco posseduto al processo 4.
1
5
9
13
2
6
10
14
3
7
11
14
4
8
12
16
int getRankRowDest(int rank, int np){
int rank_dest;
int proc_lato=(int)sqrt(np);
//caso elemento ad inizio riga
if((rank%proc_lato)==0)
rank_dest=rank+(proc_lato-1);
else
rank_dest = rank-1;
return rank_dest;
}
22
Il processo da cui ricevere il blocco è quello a destra tranne il caso particolare dove il processo è
nell’ultima posizione della riga: il processo mittente è quello all’inizio della riga.
Nell’esempio fornito, il processo 4 deve ricevere il blocco dal processo 1.
1
5
9
13
2
6
10
14
3
7
11
14
4
8
12
16
// determinazione del rank del processo da cui ricevere il sottoblocco della
matrice A
int getRankRowMit(int rank, int np){
int rank_mit;
int proc_lato=(int)sqrt(np);
if((rank+1)%proc_lato==0) // se siamo sull'ultima colonna
rank_mit=rank-(proc_lato-1);
else
rank_mit=rank+1;
return rank_mit;
}
Per realizzare invece lo scambio dei blocchi tra processi della matrice B, ogni processo spedisce il
sottoblocco posseduto a quello in alto e riceve il nuovo blocco dal processo in basso.
A tale scopo sono state implementate due funzioni:
getRankColDest determina il rank del processo a cui devo inviare il sottoblocco della
matrice B
getRankColMit specifica il rank del processo da cui devo ricevere il sottoblocco della
matrice B
// determinazione del rank del processo a cui devo inviare il sottoblocco di B
posseduto
col_dest=getRankColDest(rank,num_proc);
// determinazione del rank del processo da cui ricevere il sottoblocco di B
col_mit=getRankColMit(rank,num_proc);
Ad esempio, consideriamo una esecuzione con 16 processi:
1
5
9
13
2
6
10
14
3
7
11
14
4
8
12
16
Rank processo
1
5
9
13
Da chi devo ricevere
5
9
13
1
A chi devo spedire
13
1
5
9
23
Il processo a cui inviare il blocco è quello in alto tranne il caso particolare in cui il blocco si trova
nella prima posizione della colonna: il processo destinatario è l’ultimo della colonna.
Nell’esempio, il processo 1 deve inviare il blocco posseduto al processo 13.
1
5
9
13
2
6
10
14
3
7
11
14
4
8
12
16
// determinazione del rank del processo a cui inviare il proprio sottoblocco
della matrice B
int getRankColDest(int rank,int np){
int rank_dest;
int proc_lato=(int)sqrt(np);
if (rank < proc_lato)
rank_dest=np-(proc_lato-rank);
else
rank_dest=rank-proc_lato;
return rank_dest;
}
Il processo da cui ricevere il blocco è quello in basso tranne il caso particolare nel quale il processo
è nell’ultima posizione della colonna: il processo mittente è quello all’inizio della colonna.
Nell’esempio fornito, il processo 13 deve ricevere il blocco dal processo 1.
// determinazione del rank del processo da cui ricevere il sottoblocco della
matrice B
int getRankColMit(int rank, int np){
int rank_mit;
int proc_lato=(int)sqrt(np);
if(rank>=(np-proc_lato))
rank_mit=(rank+proc_lato)%np;
else
rank_mit=rank+proc_lato;
return rank_mit;
}
Conosciuti i rank dei mittenti e dei destinatari, il passo successivo è la spedizione e la ricezione dei
blocchi corrispondenti. Per realizzate tutto questo è stata utilizzata la funzione
MPI_Sendrecv_replace.
24
// invio e ricezione del blocco A
MPI_Sendrecv_replace(subArrA,b,MPI_DOUBLE,row_dest,1,row_mit,1,MPI_COMM_WO
RLD,&statA);
// invio e ricezione del blocco B
MPI_Sendrecv_replace(subArrB,b,MPI_DOUBLE,col_dest,1,col_mit,1,MPI_COMM_WO
RLD,&statB);
E’ stato deciso di adottare MPI_sendrecv_replace perché MPI_Send e MPI_recv non possono
essere utilizzati per la comunicazione punto a punto poiché se tutti i processi invocano MPI_Send o
MPI_Recv, in un qualche ordine, si può verificare una situazione di deadlock.
L’ultimo passo all’interno del ciclo for è quello di convertire i due sottoblocchi ricevuti (uno della
matrice A e l’altro della matrice B) in forma matriciale dato che sono stati ricevuti due array.
Tale compito è realizzato dalla funzione array2Mat.
// conversione dell'array corrispondente al blocco della matrice A ricevuto in
matrice
array2Mat(ricA,subArrA,l);
// conversione dell'array corrispondente al blocco della matrice B ricevuto in
matrice
array2Mat(ricB,subArrB,l);
Ad esempio se è stato ricevuto il seguente array subArrA viene convertito nella seguente matrice:
1
2
5
6
1
5
2
6
Al termine del ciclo for, la matrice c_final contiene il sottoblocco della matrice C che il processo
doveva calcolare.
[master e slave] Spedizione blocco calcolato al master
Ogni processo possiede il sottoblocco da spedire al master e per prima cosa lo deve convertire in
array per la spedizione.
double *subArrC;
// conversione del blocco della matrice C calcolata dal processo
subArrC=subMat2subArray(calC);
25
77
79
78
80
77
78
79
80
subArrC
Attraverso la funzione MPI_Gather ogni processo spedisce il proprio array al master.
// spedizione
sottoblocchi
del
sottoblocco
calcolato
della
matrice
C
e
ricezione
dei
MPI_Gather(subArrC,b,MPI_DOUBLE,arrC,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
[master]
Raccolta di tutti i blocchi e creazione della matrice C
Il master tramite l’invocazione della funzione MPI_Gather raccoglie tutti i sottoblocchi (spediti
sottoforma di array) e li memorizza nell’array arrC ordinati per numero del rank dei processi
mittenti.
MPI_Gather(subArrC,b,MPI_DOUBLE,arrC,b,MPI_DOUBLE,0,MPI_COMM_WORLD);
Rank 0
22
23
24
25
Rank 1
26
27
28
29
Rank 2
30
31
32
33
Rank 3
34
34
36
37
arrC
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Ottenuto l’array rimane da convertirlo in matrice: la matrice finale C = A × B. Tale conversione
viene realizzata dalla funzione array2MatFormatted.
// conversione dell'array in matrice
matC=array2MatFormatted(arrC,matA->dim,num_proc);
26
arrC
22
23
24
25
26
27
28
29
30
22
24
30
32
23
25
31
33
26
28
34
36
27
29
35
37
31
32
33
34
35
36
37
27
Processor Farm
Funzionamento
Nell’algoritmo Processor Farm (o Worker Pool), a differenza degli altri due algoritmi,
l’assegnazione dei task avviene in maniera dinamica, consentendo così un miglior bilanciamento
del carico.
In questo algoritmo il processo master non si occupa di eseguire i calcoli parziali delle matrici, ma
solo di distribuire i task agli slaves e di raccogliere i risultati. I processi slaves invece si occupano
dei calcoli parziali della matrice C inviando poi i risultati al processo master.
Il primo passo di questo algoritmo consiste nel replicare la matrice B a tutti i worker.
Successivamente, il processo master distribuisce a tutti gli altri processi, ad ogni passo, una riga
della matrice A. Gli slaves, una volta ricevuta la riga, effettuano il calcolo vettore per matrice, tra la
riga ricevuta e la matrice B e spediscono il risultato al processo master che a sua volta spedisce
un’altra riga di A se ne sono disponibili ancora. L’algoritmo termina una volta esaurite le righe
della matrice A.
In particolare:
• Viene replicata la matrice B a tutti i worker.
• I task consistono nel calcolare k righe della matrice C con k < (n / p ) , dove n è la
dimensione del lato della matrice e p è il numero di processi.
• Il processo master assegna dinamicamente i task ai worker inviando k righe della matrice A.
28
C
A
B
K
Master
Riga
di C
Riga
di C
Riga
di A
Riga
di A
B
B
Slave 1
Slave n
Riassumendo:
[master]
[master]
[master]
[slaves]
[slaves]
[slaves]
[master]
[master]
[master]
Lettura delle matrici A e B
Replica della matrice B a tutti gli altri worker
Invio deii task a tutti i worker disponibili (k righe)
Ricezione della riga della matrice A
Prodotto vettore per matrice (riga di A per matrice B)
Invio dei risultati e attesa di una eventuale nuova riga
Finché non ho ricevuto tutte le righe della matrice A
Ricezione dei risultati parziali e aggiornamento della matrice C
Invio di eventuali altre righe della matrice A
29
[master]
Lettura delle matrici A e B e replica della matrice B
Il master è l’unico che può accedere alle matrici A e B. La lettura di tali matrici avviene da disco se
è settata la variabile FROM_FILE altrimenti vengono generate automaticamente della dimensione
specificata in DIM.
La matrice B viene spedita tramite la funzione MPI_BCast( ); prima della spedizione della matrice
vengono effettuate due operazioni:
1. Per effettuare la distribuzione della matrice B, siccome siamo in presenza di un contesto
distribuito, è necessaria per prima cosa comunicare a tutti gli slave quale è la dimensione
della matrice che gli viene assegnato tramite la funzione di MPI_BCast():
// assegno a dim la dimensione della matrice B
dim= matB->dim
// distribuzione della dimensione della matrice
MPI_Bcast (&dim, 1, MPI_INT, 0, MPI_COMM_WORLD);
2. La matrice B viene convertita in array tramite la funzione subMat2subArray(matB).
// conversione matrice B in array
arrB = (double*) subMat2subArray(matB);
L’array arrB, che rappresenta la matrice B,viene poi spedito a tutti gli altri worker tramite la
funzione MPI_BCast()
//spedizione e ricezione della matrice MatB
MPI_Bcast(arrB, dim*dim, MPI_DOUBLE, 0, MPI_COMM_WORLD);
[master]
Invia i task a tutti i worker disponibili (k righe)
In questa fase, la prima cosa fatta è controllare se il numero di processi allocati sia sufficiente
affinché possa essere eseguito l’algoritmo. Il numero di processi deve essere almeno due: uno per il
master e uno che effettua la computazione.
Successivamente avviene l’assegnazione statica dei task ai vari processi. Questo problema si divide
in due casi:
1. Il numero di righe della matrice A è minore rispetto al numero di processi allocati. In questo
caso tutti i processi che non eseguano la computazione devono essere settati subito allo stato
di IDLE
2. Il numero di righe della matrice A è maggiore o uguale al numero di processi allocati. In
questo caso tutti i processi partecipano alla computazione.
30
Viene creato un array (map) per mantenere la corrispondenza tra il processo e il numero di riga
assegnata. In questo modo è possibile sapere in ogni momento quale processo computa una
determinata riga della matrice A.
Quindi map [i] = j significa che il processo i ha computato la riga j della matrice A.
//Map mantiene la corrispondenza tra processo e riga della matrice assegnata
map=(int*)calloc(num_proc,sizeof(int));
// il master non effettua il prodotto delle matrici
map[0]=0;
Viene poi effettuata l’operazione di assegnamento delle righe ai processi:
for (i=0;i<limite;i++){
prelevaRiga(matA, i, rowA);
MPI_Send(rowA, dim, MPI_DOUBLE, i+1, INVIO, MPI_COMM_WORLD);
//corrispondenza riga--->processo
map[i+1]=i
}
L’i-esima riga della matrice A viene prelevata tramite la funzione prelevaRiga( ) e inviata al
processo i+1-esimo (i+1 perché il processo 0 essendo il master non effettua la computazione).
Inoltre viene aggiornata la corrispondenza processo – riga.
Come detto in precedenza nel caso in cui il numero di righe sia inferiore al numero di processi
allocati, tutti quelli che non lavorano vengono settati a IDLE. Tutto questo viene fatto inviando al
processo un array contenete tutti valori nulli (il vettore riga_zero); tale messaggio inviato ha tag
IDLE.
//caso numero processori maggiore del numero di righe della matrice, setto a
idle tutti i processori che non lavorano
if(dim <= num_proc-1){
int q;
for(q=limite+1;q<num_proc;q++){
MPI_Send(riga_zero, dim, MPI_DOUBLE, q, IDLE, MPI_COMM_WORLD);
}
[slaves]
operazioni lato slaves
I processi slaves compiono queste operazioni:
1.
2.
3.
4.
ricevono la dimensione della matrice B e tutta la matrice B;
ricevono la riga della matrice A;
effettuano il prodotto vettore per matrice (riga della matrice A per matrice B);
inviano il vettore risultato al master e si mettono in attesa di una nuova riga da calcolare
finché non vengono settati dal master allo stato di IDLE.
La ricezione della dimensione della matrice B e di tutta la matrice stessa avviene tramite
l’operazione di collettiva MPI_BCast( ); I processi non ricevono la matrice vera e propria ma un
31
vettore che rappresenta la matrice. Tale array verrà poi convertito in matrice da ogni worker
attraverso la funzione arrayToMat().
MPI_Bcast (&dim, 1, MPI_INT, 0, MPI_COMM_WORLD);
arrB = (double *) calloc(dim*dim, sizeof(double));
//ricezione della matrice Matrice B
MPI_Bcast(arrB, dim*dim, MPI_DOUBLE, 0, MPI_COMM_WORLD);
ricB = (matrice *) malloc (sizeof(matrice));
array2Mat(ricB,arrB,dim);
La seconda operazione è quella di ricevere una riga della matrice A per poi successivamente farne il
prodotto con la matrice B. Tale operazione continua ad avvenire fino a che il worker continua
ricevere dati dal master. Quando il master ha terminato le righe, il processo worker riceve un vettore
nullo e viene settato allo stato di IDLE
riga_temp = (double*) calloc (dim, sizeof(double));
riga_ris = (double*) calloc (dim, sizeof(double));
MPI_Recv(riga_temp, dim, MPI_DOUBLE, 0 , MPI_ANY_TAG , MPI_COMM_WORLD, &statA);
// Fino a che ricevo dati
while (statA.MPI_TAG == INVIO){
//Esecuzione del prodotto della riga della matrice A ricevuto con la matrice B
ProdVetMat(riga_temp, ricB, riga_ris);
//Invio della riga calcolata e attesa della prossima riga
MPI_Send(riga_ris, dim, MPI_DOUBLE, 0, RICEZIONE, MPI_COMM_WORLD);
MPI_Recv(riga_temp, dim, MPI_DOUBLE,0,MPI_ANY_TAG,MPI_COMM_WORLD, &statA);
}
La riga ricevuta dal processo master viene memorizzata nell’array riga_temp, mentre il risultato del
prodotto riga per matrice che viene calcolato attraverso la funzione ProdVetMat() e viene
memorizzato nell’array riga_ris. Il risultato viene spedito al processo master attraverso la funzione
MPI_Send(), mentre la ricezione delle righe viene effettuata attraverso la funzione MPI_Recv().
Il controllo sul tipo di dato ricevuto o inviato viene fatto attraverso la verifica dello stato del valore
del tag associato al messaggio: i valori del tag sono tre:
1. INVIO significa che sono stati spediti dei dati da computare (da master a slaves);
2. RICEZIONE significa che sono stati spediti dati già computati (da slaves a master);
3. IDLE significa che è un messaggio che setta a IDLE un processo (da master a slaves).
Il processo, se non è idle, si mette poi in attesa di un nuovo messaggio. I messaggi che può ricevere
sono di due tipi:
1. un’altra riga della matrice A da computare. In questo caso il messaggio ha tag di tipo
INVIO;
2. un messaggio contenente un array nullo con associato un tag di tipo IDLE. In questo
caso il processo viene settato allo stato di IDLE.
Il processo, quindi non sapendo a priori quale tipo di messaggio può ricevere, associa alla primitiva
MPI_Recv( ) il tag di tipo MPI_ANY_TAG. In questo modo il tag del messaggio ricevuto può
essere qualsiasi.
32
[master]
Raccolta dei risultati
In questa sezione il master attraverso la funzione MPI_Recv ( ) attende tutti i risultati delle righe
della matrice A spedite precedentemente e calcolati dai processi slaves. L’attesa procede finché il
numero di righe ricevute non raggiunge il numero di righe (la dimensione del lato) della matrice A.
In questo caso il messaggio ha tag di tipo RICEZIONE. Infatti il processo è in attesa di una riga già
calcolata.
Il processo master non conosce a priori nemmeno da chi riceverà la riga calcolata, visto che
distribuisce le righe a più processi. Quindi il campo mittente della funzione MPI_Recv ha valore
MPI_ANY_SOURCE che identifica che il mittente del messaggio può essere uno qualsiasi.
//Fino a il master non ha ricevuto tutte le righe dagli slaves
while (num_righe_rec < dim){
MPI_Recv(riga_temp,dim,MPI_DOUBLE,MPI_ANY_SOURCE,RICEZIONE,MPI_COMM_WORLD,
&statB);
Ricevuta una riga calcolata, il processo master inserisce la righe nella matrice C (lo spazio
necessario per la matrice deve essere già stato allocato). L’inserimento avviene attraverso l’utilizzo
della funzione settaRiga( ) che inserisce la riga riga_temp nella posizione specificata dall’array map
che era stato precedentemente aggiornato. L’indice di map, che rappresenta il processo che ha
calcolato la riga, può essere trovato estraendo dallo stato del messaggio appena ricevuto il valore
MPI_SOURCE che rappresenta il mittente del messaggio.
settaRiga(matC, riga_temp, map[statB.MPI_SOURCE]);
Riassumendo nella matrice C, alla posizione map[statB.MPI_SOURCE], viene inserita la riga
calcolata riga_temp.
A questo punto, se sono disponibili altre righe della matrice A da calcolare, al processo che ha
inviato l’ultima riga calcolata, viene spedita un’altra riga oppure viene inviato un messaggio che lo
setta a IDLE.
//Se sono disponibili ancora righe da inviare /calcolare
if(num_righe_send < dim){
prelevaRiga(matA, num_righe_send, rowA);
MPI_Send(rowA, dim, MPI_DOUBLE, statB.MPI_SOURCE, INVIO, MPI_COMM_WORLD);
map[statB.MPI_SOURCE]=num_righe_send;
//aggiorno il numero di righe inviate
num_righe_send++;
}
else{
//altrimenti setto a IDLE i processi che hanno finito di lavorare
MPI_Send(riga_zero,dim,MPI_DOUBLE,statB.MPI_SOURCE,IDLE,MPI_COMM_WORLD);
}
33
Algoritmi Sequenziali per la moltiplicazione delle matrici
Algoritmo normalmente utilizzato
n −1
Il prodotto C delle matrici A e B è definito come cij = ∑ a ik × bkj , dove aij, bij, and cij sono gli
k =0
elementi dell’ i-esima riga e della j-esima colonna delle matrici A, B e C rispettivamente.
Per poter eseguire la moltiplicazione delle matrici, la dimensione di AB=C deve soddisfare
(n × m)(m × p ) = (n × p ) . Per semplicità, supponiamo di utilizzare matrici quadrate: in tal caso le
matrici A, B e C sono tutte n × n.
L’algoritmo sequenziale per la moltiplicazione della matrici può essere descritto nel seguente modo:
for i = 0 to n-1
for j = 0 to n-1
cij =0
for k = 0 to n-1
cij = cij + aik × bkj
Tale algoritmo richiede n3 addizioni e moltiplicazioni: la complessità è dell’ordine di O(n3).
Algoritmo di Strassen
L’algoritmo di Strassen reduce la complessità a O(n2.81). In questo algoritmo, le matrici n × n sono
suddivise in quattro n/2 × n/2 sottomatrici.
La figura sotto riportata mostra una moltiplicazione di matrici 2 × 2.
Poiché tale algoritmo divide le matrici in 2 e le moltiplica ricorsivamente, per poter eseguire tali
operazioni, le matrici devono essere quadrate e la loro dimensione deve essere una potenza di 2.
Tale limitazione può essere superata tramite l’utilizzo dell’algoritmo di Winograd, una variante
dell’algoritmo di Stassen.
Se le matrici A e B sono della dimensione 2dm × 2dm, l’algoritmo di Stassen viene eseguito
ricorsivamente d volte. Dopo d ritorsioni, l’algoritmo standard è utilizzato per la moltiplicazione delle
matrici m × m.
34
Un esempio di esecuzione dell’algoritmo di Stassen per la moltiplicazione delle matrici di dimensione 2 × 2
è riportato qui sotto. Necessita di 7 moltiplicazioni e 18 addizioni/sottrazioni.
 c00c01   a00 a01  b00b01 

 = 


c
c
a
a
b
b
 10 11   10 11  10 11 
S1 = a10 + a11
S2 = S1 – a00
S3 = a00 – a10
S4 = a01 – S2,
P1 = a00b00
P2 = a01b10
P3 = S1T1
P4 = S2T2
P5 = S3T3
P6 = S4b11
P1 = a11T4
Algorito di Stassen per la mo
c00 = U1
c10 =U4
T1 = b01 – b00
T2 = b11 – T0
T1 = b11 – b01
T1 = b10 – T1
U1 = P1 + P2
U2 = P1 + P4
U3 = U2 + P5
U4 = U3 + P7
U5 = U3 + P3
U6 = U2 + P3
U7 = U6 + P6
c01 = U7
c11=U5
35
Test progetto
Gli obiettivi dei test è la valutazione delle seguenti caratteristiche:
l’eterogeneità e lo sbilanciamento del cluster
lo speedup con differenti granularità e differenti dimensioni del problema
Per tale motivo, le dimensioni delle matrici (dense) scelte sono:
384
768
1152
1920
Questo permette di valutare ogni algoritmo con una variazione consistente della dimensione delle
matrici e quindi del relativo costo computazionale: si parte da una matrice di dimensioni ridotte fino
ad arrivare ad una di dimensioni elevate.
Dato che la moltiplicazione delle matrici non è sufficiente per ottenere un vantaggio sull’utilizzo
degli algoritmi paralleli è stata implementata una funzione f, detta di rallentamento, in modo da
eseguire C = f(A) × B invece che C = A× B.
Tale funzione di rallentamento può eseguire il seguente numero di cicli:
0
5
10
40
(non presenza)
Il cluster sul quale sono stati eseguiti i test
utilizza macchine Linux con kernel 2.6.17.8
accesso remoto a tale cluster avviene attraverso [email protected].
Principali grafici realizzati con relativi commenti
Di seguito vengono riportati i grafici delle prestazioni dei 3 algoritmi implementati, ottenuti
attraverso i test effettuati.
36
Commento Grafici Numero processi vs. tempo
Matrice 384
Come si può notare dal primo grafico, con matrici di dimensioni ridotte e quindi con carico
computazionale molto limitato, è possibile constatare che passando da 4 a 9 processi il tempo di
esecuzione rimane pressoché invariato, mentre nel passaggio da 9 a 16 il tempo di esecuzione degli
Algoritmi Processor Farm e Matrix Matrix aumenta, quello dell’altro algoritmo rimane costante.
Questo comportamento anomalo è dovuto al maggior numero di comunicazioni che gli algoritmi
devono effettuare all’aumentare del numero di processi; questo si nota maggiormente nel Processor
Farm visto che bisogna inviare l’intera matrice B ad ogni processo creando così un rallentamento
del processo master con conseguente degradazione delle prestazioni.
Matrice 384 − Funzione rallentamento disattivata
2
Matrix Multiply
Cannon Algorithm
Processor Farm
1.8
1.6
1.4
Tempo (sec)
1.2
1
0.8
0.6
0.4
0.2
0
4
6
8
10
Numero Processi (np)
12
14
16
La situazione migliora con l’attivazione della funzione di rallentamento, infatti il costo
computazionale aumenta rispetto ai costi di comunicazione e quindi traiamo vantaggio
nell’esecuzione con un numero di processi maggiore
37
Matrice 384 − Funzione rallentamento attivata a 40 cicli
5
Matrix Multiply
Cannon Algorithm
Processor Farm
4.5
4
3.5
Tempo (sec)
3
2.5
2
1.5
1
0.5
0
4
6
8
10
Numero Processi (np)
12
14
16
. Matrice 1920
38
Matrice 1920 − Funzione rallentamento disattivata
300
Matrix Multiply
Cannon Algorithm
Processor Farm
250
Tempo (sec)
200
150
100
50
0
4
6
8
10
12
Numero Processi (np)
14
16
In questa situazione possiamo notare che anche se la funzione di rallentamento non è attivata, grazie
al maggior costo computazionale dovuto all’aumento della dimensione della matrice, il tempo di
esecuzione diminuisce al crescere del numero dei processi.
Il miglioramento delle prestazioni è dovuto alla minore quantità di dati elaborati da ogni processo
poiché sono a disposizione più processi.
Il miglioramento più accentuato del Processor Farm può essere giustificato dal maggior numero di
righe che vengono processate parallelamente. Infatti, nel caso di 4 processi, un processo ha il
compito di gestire le comunicazioni delle righe e solo 3 processi sono disponibili per il calcolo. Con
9 processi sono disponibili ben 8 processi che possono eseguire i calcoli parallelamente.
Il Processor Farm non riesce comunque a raggiungere le prestazioni degli altri algoritmi perché le
comunicazioni hanno ancora costi maggiori rispetto ai costi computazionali.
39
Matrice 1920 − Funzione rallentamemto attivata a 40 cicli
Matrix Multiply
Cannon Algorithm
Processor Farm
700
600
Tempo (sec)
500
400
300
200
100
0
4
6
8
10
12
Numero Processi (np)
14
16
Possiamo notare infatti che abilitando la funzione di rallentamento a 40 cicli, abbiamo numero di
computazioni sufficienti a rendere il Processor Farm più performante rispetto agli altri due.
40
Commento Grafici Ritardo vs. tempo
Numero processi 4
Matrice 384 − Numero Processi 4
5
Matrix Multiply
Cannon Algorithm
Processor Farm
4.5
4
3.5
Tempo (sec)
3
2.5
2
1.5
1
0.5
0
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
41
Matrice 1152 − Numero Processi 4
150
Matrix Multiply
Cannon Algorithm
Processor Farm
Tempo (sec)
100
50
0
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
Si noti che con un numero di processi pari a 4, aumentando il numero di cicli eseguiti dalla funzione
di rallentamento, cresce il maniera lineare il tempo impiegato dai vari algoritmi, con la matrice di
arietà minore. Tale crescita è molto accentuata fino ad un ritardo pari a 10 cicli dopodiché tende a
crescere meno rapidamente.
In entrambi i grafici il Processor Farm ha prestazioni peggiori.
I risultati del primo grafico sono dovuti al tempo di comunicazione che rimane invariato, mentre il
tempo di computazione in ogni processo viene incrementato in maniera lineare attraverso la
funzione di rallentamento.
Un discorso simile può essere fatto per il secondo grafico.
Il tempo superiore impiegato dal Processor Farm è dovuto al numero limitato di processi disponibili
(in questo caso 3 invece che 4).
42
Numero processi 16
Matrice 1920 − Numero processi 16
200
Matrix Multiply
180
Cannon Algorithm
Processor Farm
160
Tempo (sec)
140
120
100
80
60
40
20
0
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
Quanto detto prima, può essere confermato dalla figura sopra riportata; grazie al maggior numero
dei processi a disposizione, il Processor Farm raggiunge prestazioni più elevate rispetto agli altri
algoritmi.
Grafici dimensione matrice – tempo
Funzione di rallentamento disattivata
43
Funzione rallentamento disattivata − numero processi 4
300
Matrix Multiply
250
Cannon Algorithm
Processor Farm
Tempo (sec)
200
150
100
50
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Con 4 processi e con una funzione di rallentamento disattivata, all’aumentare della dimensione della
matrice il tempo di esecuzione dei tre algoritmi aumenta.
Tale aumento è dovuto all’incremento della dimensione dei sottoblocchi spediti, negli algoritmi
Matrix Matrix e Cannon mentre nel Processor Farm viene aumentata sia la dimensione dei dati
inviati (dato che la dimensione delle righe è maggiore), sia il numero di comunicazioni necessarie
(incremento numero righe) .
Nel Processor Farm le prestazioni sono peggiori grazie all’elevato numero di comunicazioni
eseguite.
Gli algoritmi Matrix Matrix e Cannon hanno prestazioni, in questo caso, molto simili perché il
numero di comunicazioni eseguite e la dimensione dei dati inviati si equivalgono.
La differenza di prestazioni tra l’algoritmo Processor Farm e gli altri due aumenta con la crescita
della dimensione della matrice; questo succede perché il costo di computazione è basso e quindi il
tempo di comunicazione tende a “sovrastare” quest’ultimo.
44
Funzione rallentamento disattivata − numero processi 16
70
Matrix Multiply
Cannon Algorithm
60
Processor Farm
Tempo (sec)
50
40
30
20
10
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Si noti che con 16 processi, il divario tra il tempo di esecuzione del Processor Farm e quello degli
altri algoritmi diminuisce dato che gli algoritmi vengono eseguiti su di un numero maggiore di
processi e quindi aumentano il numero delle comunicazioni da eseguire negli algoritmi Matrix
Matrix e Cannon.
Funzione di rallentamento attivata
45
Funzione rallentamento attivata a 40 cicli − numero processi 16
200
180
Matrix Multiply
Cannon Algorithm
160
Processor Farm
Tempo (sec)
140
120
100
80
60
40
20
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Con la funzione di rallentamento attivata a 40 cicli, viene aumentato il tempo necessario alla
computazione e questo fa si che le performance del Processor Farm si avvicinino a quelle degli altri
algoritmi, fino a diventare le migliori.
Dal passaggio da una situazione senza funzione di rallentamento a una con la funzione di
rallentamento di 40 cicli, il costo delle comunicazioni è invariato ma quello del calcolo aumenta
notevolmente. In questa situazione, trae vantaggio l’algoritmo Processor Farm perché il prodotto
vettore per matrice ha complessità minore rispetto al prodotto matrice per matrice.
Grafici dimensione numero processori – speedup
Matrice 384
46
Calcolo Speedup − Funzione disattivata − Matrice: 384 X 384
2.5
Speedup (Sp(n))
2
Matrix Multiply
Cannon Algorithm
1.5
1
Processor Farm
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Speedup − Funzione attivata 40 cicli − Matrice: 384 X 384
10
9
8
Speedup (Sp(n))
7
6
5
Matrix Multiply
Cannon Algorithm
4
Processor Farm
3
2
1
4
6
8
10
12
Numero Processori (np)
14
16
47
Nel caso della matrice di dimensione 384 con funzione disattivata, il problema risulta troppo
piccolo per ottenere degli speedup lineari. Appare evidente che il tempo di comunicazione incida
nettamente rispetto al tempo di computazione. Infatti all’aumentare del numero di processi, e quindi
dei tempi di comunicazione, lo speedup decresce.
Attivando però la funzione di rallentamento (nel caso a 40 cicli) , e quindi aumentando il peso
computazionale, i risultati migliorano ottenendo anche degli speedup lineari nel caso in cui il costo
per la comunicazione è basso ( uso di 4 processi ). Questo è dovuto dal fatto che l’algoritmo
sequenziale è limitato dalla CPU, mentre gli algoritmi paralleli possono suddividere il carico ai vari
processori disponibili.
Si potrebbe pensare che all’aumentare del numero di processi lo speedup si mantenga lineare, ma
questo non accade perché il peso computazionale rispetto a quello di comunicazione risulta
inferiore (la matrice 384 è troppo piccola).
Matrice 1152
Calcolo Speedup − Funzione F attivata : 40 − Matrice: 1152 X 1152
12
11
10
Speedup (sp(n))
9
8
7
6
5
Matrix Multiply
4
Cannon Algorithm
Processor Farm
3
2
1
4
6
8
10
12
Numero Processori (np)
14
16
Con questa matrice di grandi dimensioni e funzione f attivata a 40 cicli riusciamo ad avere un
calcolo computazionale elevato, avvantaggiando quindi tutti gli algoritmi paralleli. Infatti come
possiamo vedere in figura nel caso in cui i processi variano da 4 a 9 riusciamo ad avere degli
speedup lineari (per gli algoritmi Matrix Matrix e Cannon), mentre nel passaggio da 9 a 16 processi
lo speedup ottenuto tende a diminuire (rispetto a quello lineare) tranne nel caso dell’algoritmo
Processor Farm (che mantiene una crescita lineare ma non raggiunge mai uno speedup lineare,
neanche nel caso di 4 e 9 processi). Questa diminuzione è dovuta sempre dal fatto che all’aumentare
del numero di processi aumenta il costo di comunicazione e diminuisce il costo del calcolo e quindi
gli overhead di comunicazione diventano la frazione maggiore del tempo totale. L’algoritmo
48
Processor Farm mantiene una crescita dello speedup lineare perché il numero di comunicazioni
all’aumentare del numero di processi rimane invariato, ma il singolo processo esegue globalmente
un numero di computazioni inferiori.
Matrice 1920
Calcolo Speedup − Funzione F disattivata − Matrice: 1920 X 1920
25
Matrix Multiply
Cannon Algorithm
Speedup (sp(n))
20
Processor Farm
15
10
5
2
4
6
8
10
Numero Processori (np)
12
14
16
49
Calcolo Speedup − Funzione F attivata : 10 − Matrice: 1920 X 1920
25
Matrix Multiply
Cannon Algorithm
Processor Farm
Speedup (sp(n))
20
15
10
5
4
6
8
10
12
Numero Processori (np)
14
16
50
Con una funzione di rallentamento disattivata lo speedup in questo caso è superlineare. Il motivo
dello speedup superlineare è l’utilizzo di un algoritmo sequenziale non ottimo.
L’algoritmo utilizzato nel nostro caso per moltiplicazione tra matrici, ha complessità O(m3) dove m
è la dimensione della matrice. Un algoritmo che ha complessità asintotica minore è l’algoritmo di
Strassen, introdotto nel 1969, che ha complessità O(mlg(7)) dove lg(x) denota il logaritmo in base 2
di x e lg(7) ≈ 2,807. Non è stato utilizzato questo algoritmo perché è molto difficile da
implementare e richiede un notevole utilizzo della memoria.
Se fosse stato utilizzato questo algoritmo, probabilmente, lo speedup raggiungerebbe risultati più
consoni, anche negli altri casi.
All’aumentare della numero di cicli eseguiti dalla funzione di rallentamento, però, lo speedup tende
a migliorare, diventando in alcuni casi addirittura lineare, come ad esempio quando la funzione di
rallentamento è a 10 cicli con un numero di processi pari a 16.
I risultati dell’algoritmo Processor Farm sono peggiori rispetto agli altri due algoritmi. Le
prestazioni tendono comunque a migliorare aumentando il numero di cicli della funzione di
rallentamento e nel caso in cui il numero è 40 raggiunge risultati leggermente migliori rispetto agli
algoritmi Matrix Matrix e Cannon.
Grafici dimensione numero processori – efficienza
Essendo i risultati dell’efficienza strettamente legati ai risultati dello speedup, quando quest’ultimo
è minore del numero di processi abbiamo un’efficienza minore di 1 mentre nel caso di speedup
lineare l’efficienza tende al valore 1. Infine nel caso di speedup superlineare l’efficienza ha valori
maggiori di 1.
Quindi riportiamo i grafici dell’efficienza solo per completezza senza commenti. I commenti sono
gli stessi dei grafici dello speedup.
51
Matrice 384
Calcolo Efficenza − Funzione F disattivata : − Matrice: 384 X 384
1
Matrix Multiply
Cannon Algorithm
Processor Farm
0.9
0.8
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
Numero Processori (np)
12
14
16
52
Calcolo Efficenza − Funzione F attivata : 40 − Matrice: 384 X 384
1
Matrix Multiply
Cannon Algorithm
Processor Farm
0.9
0.8
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
Numero Processori (np)
12
14
16
Matrice 1152
53
Matrice 1190
54
Calcolo Efficenza − Funzione F disattivata − Matrice: 1920 X 1920
2.5
Matrix Multiply
Cannon Algorithm
Efficenza (E(n))
2
Processor Farm
1.5
1
0.5
0
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Efficienza − Funzione arrivata 10 cicli − Matrice: 1920 X 1920
2.5
Matrix Multiply
2
Cannon Algorithm
Efficenza (E(n))
Processor Farm
1.5
1
0.5
0
4
6
8
10
12
Numero Processori (np)
14
16
55
Calcolo Efficienza − Funzione arrivata 40 cicli − Matrice: 1920 X 1920
2
1.8
Matrix Multiply
Cannon Algorithm
1.6
Processor Farm
Efficenza (E(n))
1.4
1.2
1
0.8
0.6
0.4
0.2
0
4
6
8
10
12
Numero Processori (np)
14
16
56
Valutazione bilanciamento
Matrice 384
Matrice 384 − Funzione rallentamento disattivata − Numero processi = 4
1.2
1.1
Tempo (sec)
1
0.9
0.8
Matrix Multiply
Cannon Algorithm
Processor Farm
0.7
0.6
0.5
0
0.5
1
1.5
Rank processo
2
2.5
3
57
Matrice 384 − Funzione rallentamento 40 cicli − Numero processi = 4
4.5
Matrix Multiply
Cannon Algorithm
Processor Farm
Tempo (sec)
4
3.5
3
2.5
0
0.5
1
1.5
Rank processo
2
2.5
3
Con un numero di processi pari a 4 e con una matrice di dimensione relativamente piccola (384) nel
caso dell’algoritmo Processor Farm il carico risulta essere ben bilanciato tra tutti i nodi del cluster.
Per quanto concerne gli algoritmi Matrix Matrix e Cannon risultano avere un lieve sbilanciamento e
il pattern seguito da questi due algoritmi risulta essere simile.
58
Matrice 384 − Funzione rallentamento disattivata − Numero processi = 16
1.4
Matrix Multiply
Cannon Algorithm
Processor Farm
1.3
1.2
Tempo (sec)
1.1
1
0.9
0.8
0.7
0.6
0.5
0.4
0
5
10
15
Rank processo
Matrice 384 − Funzione rallentamento 40 cicli − Numero processi = 16
2
Matrix Multiply
Cannon Algorithm
Processor Farm
1.9
1.8
Tempo (sec)
1.7
1.6
1.5
1.4
1.3
1.2
1.1
1
0
5
10
15
Rank processo
59
All’aumentare del numero dei processi la situazione risulta essere simile a quella descritta in
precedenza.
In tutti e tre gli algoritmi il tempo impiegato dal processo 0 è lievemente maggiore rispetto a quello
degli altri dato che ha il compito della gestione della matrice C.
Matrice 1920
Matrice 1920 − Funzione rallentamento disattivata − Numero processi = 4
300
Tempo (sec)
250
Matrix Multiply
Cannon Algorithm
Processor Farm
200
150
100
0
0.5
1
1.5
Rank processo
2
2.5
3
60
Matrice 1920 − Funzione rallentamento 40 cicli − Numero processi = 4
750
700
Matrix Multiply
Cannon Algorithm
Processor Farm
Tempo (sec)
650
600
550
500
450
400
0
0.5
1
1.5
Rank processo
2
2.5
3
Passando ad una situazione nella quale la dimensione della matrice è maggiore si può constatare che
anche in questo caso il Processor Farm è l’algoritmo che offre un bilanciamento migliore; il
bilanciamento degli altri algoritmi comunque è più che accettabile.
61
Matrice 1920 − Funzione rallentamento disattivata − Numero processi = 16
80
70
Tempo (sec)
60
Matrix Multiply
Cannon Algorithm
Processor Farm
50
40
30
20
0
5
10
15
Rank processo
Matrice 1920 − Funzione rallentamento 40 cicli − Numero processi = 16
200
180
Tempo (sec)
160
Matrix Multiply
Cannon Algorithm
Processor Farm
140
120
100
80
0
5
10
15
Rank processo
62
In una situazione con grande granularità e soprattutto con un numero di cicli elevato, l’algoritmo
Processor Farm risulta essere ancora ben bilanciato, il Cannon ha un comportamento abbastanza
bilanciato mentre con l’algoritmo Matrix Matrix siamo in presenza di una situazione di forte
sbilanciamento del carico.
63
Caratteristiche del sistema
In questa sezione vengono descritte le caratteristiche principali del sistema implementato.
Elenco file progetto
Il codice degli algoritmi paralleli è presente nei seguenti file:
AlgoritmoMatrixMatrix.c implementazione dell’algoritmo MatrixMatrix
AlgoritmoCannon.c realizzazione dell’algoritmo Cannon
AlgoritmoWorkerPool implementazione dell’algoritmo Processor Farm
Ogni algoritmo ha definito le seguenti variabili:
#define MATRIX_A "/tmp/matriceA"
Path della matrice A scelto staticamente
#define MATRIX_B "/tmp/matriceB"
Path della matrice B scelto staticamente
#define PATH "/tmp/algo_3_"
Path dove memorizzare la matrice C (risultato) deciso staticamente
#define BENCH "/tmp/algo_bench_3_"
Path dove memorizzare il file contenente i tempi riscontrati
Per poter conoscere il tempo di esecuzione dell’algoritmo Sequenziale, utilizzato ad esempio per il
calcolo dello Speedup e dell’ Efficienza, è stato creato il file “AlgoritmoSequenziale.c”.
Siccome la lettura delle matrici deve avvenire anche da file è stata realizzato, a tale scopo, il file
“creamatrix.c”: viene richiesta la dimensione della matrice quadrata che si vuole creare e il path
dove si desidera salvarla.
Inserire la dimensione della matrice:
1920
Inserire il nome del file (percorso assoluto): /tmp/marice/MatriceCreata
Le funzioni di utilità per l’implementazione degli algoritmi sono presenti nel file “matrice.c”.
64
E’ presente anche un header “matrice.h”, il quale contiene alcuni parametri che permettono di
diversificare il comportamento degli algoritmi.
#define DEBUG (0/1)
Se settata specifica che si vuole eseguire gli algoritmi in modalità di debug.
#define TEMPO (0/1)
Nel caso in cui sia uguale ad 1 indica che vengono attivati i meccanismi per il calcolo del tempo
impiegato da ciascun processo.
#define FROM_FILE (0/1)
Se uguale a 1, le matrici A e B vengono lette da file altrimenti vengono generate in maniera
random.
#define AUTO (0/1)
Se è settata, viene richiesto il path delle matrici A e B quando vengono lette da file e il percorso
dove memorizzare la matrice C; altrimenti il path della matrice A è quello presente in MATRIX_A,
quello della matrice B in MATRIX_B e quello della matrice C in PATH.
Inoltre, se ha valore 1, il file che contiene le performance è memorizzato nel path specificato in
BENCH; in caso contrario, il path utilizzato viene specificato dall’utente.
#define DIM 768
Specifica della dimensione della matrice nel caso in cui viene generata automaticamente
(FROM_FILE = 0).
#define SLOW 1
Se ha valore 1 significa che è attivata la funzione di rallentamento.
#define RITARDO 5
Specifica del numero di cicli che deve eseguire la funzione di rallentamento
#define
#define
#define
#define
#define
#define
#define
COL(x)
COL_RED
COL_GREEN
COL_YELLOW
COL_BLUE
COL_WHITE
COL_GRAY
"\033[" #x ";1m"
COL(31)
COL(32)
COL(33)
COL(34)
COL(37)
"\033[0m"
Definizione del colore con cui si desidera stampare l’output (esempio la stampa delle matrici a
video è blu).
65
Come spiegato nel manuale d’uso, è stato creato un “makefile” per facilitare la compilazione dei
file e uno script “runalgo” per l’invocazione e l’esecuzione degli algoritmi con i parametri
desiderati.
Rappresentazione matrice
Per rappresentare una matrice è stata definita la seguente struttura:
typedef struct {
double ** val;
int dim;
}matrice;
Dove:
val matrice bidimensionale di double utilizzata per contenere i valori della matrice
dim la dimensione della matrice, ossia il numero di elementi presenti in una riga/colonna
Funzioni di utilità
Tutte le funzioni di utilità degli algoritmi sono state inserite nel file matrice.c:
InizializzaMatrice: lettura della matrice da file e allocazione di tale matrice. Viene
controllato se la matrice ha una dimensione quadrata
esci: uscita dal programma a seguito del verificarsi di un errore e stampa a schermo del
messaggio di errore verificatosi
array2Mat: trasformazione di array in una matrice
array2MatFormatted: conversione di un array in una matrice suddivisa in sottoblocchi
mat2Array: la matrice suddivisa i sottoblocchi viene convertita in un array per la spedizione
subMat2subArray: conversione di una matrice (non suddivisa in sottoblocchi) in array
prelevaPath: lettura del path della matrice inserito dell’utente
stampamatrice: stampa a video del contenuto di una matrice
stampaArray: stampa a video del contenuto di una array (in forma matriciale)
stampaArrayWP: stampa a video dell’array (non in forma matriciale)
getCoordX: determinazione della coordinata x dell’angolo in alto a sinistra del sottoblocco
assegnato al processo
66
getCoordY: determinazione della coordinata y dell’angolo in alto a sinistra del sottoblocco
assegnato al processo
allocaMatrice: allocazione dello spazio alla matrice
ProdottoMat: calcolo del prodotto tra due matrici
SommaMat: calcolo della somma tra due matrici; il risultato viene salvato nella prima
matrice A = A + B
SommaMat2: calcolo della somma tra due matrici, viene creata una nuova matrice per
contenere il risultato C = A + B
SwapRow: per l’algoritmo Cannon, allineamento della matrice A per righe
SwapCol: per l’algoritmo Cannom, allinemanto della matrice B per colonne
newCoordY: calcolo della nuova coordinata Y del sottoblocco
newCoordX: calocolo della nuova coordinata X del sottoblocco
getRankRowDest: determinazione del processo a cui inviare il sottoblocco della matrice A
getRankColDest: determinazione del processo a cui inviare il sottoblocco della matrice B
getRankRowMit: individuazione del processo da cui ricevere il sottoblocco della matrice A
getRankColMit: individuazione del processo da cui ricevere il sottoblocco della matrice B
resettaMatrice: set di tutti gli elementi della matrice a zero
checkNumProc: per l’algoritmo Processor Farm, controllo se il numero di processi sul quale
si esegue l’algoritmo è sufficiente
prelevaRiga: estrazione di una riga dalla matrice
ProdVetMat: calcolo del prodotto vettore per matrice
settaRiga: set degli elementi di una riga in base ai valori presenti nel vettore passato
liberaMemoria: libero la spazio occupato dalla matrice
buildMatrice: crea una matrice random della dimensione specificata
scriviMatrice: scrittura della matrice nel file
computabile: controllo se la matrice è computabile per gli algoritmi Matrix Matrix e Cannon
savePerformance: scrittura dei tempi eseguiti dai vari processi in un file
67
Implementazione della funzione f di rallentamento
A tutti gli algoritmi (compreso quello sequenziale) è possibile applicare una funzione f (detta di
rallentamento) a tutti gli elementi A[i,j], per incrementare la granularità dei task paralleli.
Come già spiegato, settando SLOW a 1 si applica la funzione e il numero di cicli che si vogliono
eseguire sono specificati in RITARDO.
Per realizzare la moltiplicazione gli algoritmi MatrixMatrix e Cannon utilizzano la funzione:
// calcolo del prodotto tra due matrici
matrice* ProdottoMat(matrice * a, matrice* b, int d){
double somma=0.0;
int i,j,h;
matrice *calC=(matrice*)malloc(sizeof(matrice));
calC->dim = d;
calC->val = allocaMatrice(d);
for(i=0; i<d;i++){
for(j=0;j<d; j++){
somma=0.0;
for(h=0;h<d;h++){
if(SLOW){
int g, s=0;
for(g=0;g<RITARDO;g++)
s++;
}
somma=somma+(a->val[i][h]*b->val[h][j]);
}
calC->val[i][j]= (double) somma;
}
}
return calC;
}
Invece, la funzione per la moltiplicazione utilizzata dall’algoritmo Processor Farm è la seguente:
// calcolo del prodotto vettore - matrice
void ProdVetMat(double *riga, matrice* b, double *riga_ris){
int dim = b->dim;
double somma=0.0;
int i,j;
for(i=0; i<dim;i++){
somma = 0.0;
for(j=0;j<dim;j++){
if(SLOW){
int g, s=0;
for(g=0;g<RITARDO;g++)
s++;
}
somma = somma + (riga[j] * b->val[j][i]);
}
riga_ris[i] = somma;
}
}
68
Manuale d’uso
In questa sezione vengono fornite alcune informazioni sulla compilazione e sull’utilizzo degli
programmi realizzati.
Compilazione attraverso Makefile
Il progetto realizzato include un makefile, il quale permette di compilare i file del progetto
“lanciando” in comando ‘make’ dalla directory nella quale di trovano i file.
Attraverso il comando ‘make all’ si esegue il “build” dei seguenti algoritmi:
MatrixMatrix:
Cannon:
Processor Farm:
Sequenziale:
AlgoritmoMatrixMatrix.o
AlgoritmoCannon.o
AlgoritmoWorkerPool.o
AlgoritmoSequenziale.o
Viene creato l’eseguibile anche per il file creamatrix.
Modifica header dei file
Come già specificato, modificando gli header del file “matrice.h” è possibile variare il
comportamento dei vari algoritmi.
Gli header da settare opportunamente sono:
SLOW
RITARDO
DEBUG
TEMPO
FROM_FILE
AUTO
DIM
Per il loro significato si consulti la sezione delle “Caratteristiche del sistema”.
69
Utilizzo
Per “lanciare” uno dei programmi è sufficiente invocare:
./runalgo.sh
A video compare un messaggio che spiega l’utilità di tale script:
Questo script invoca MPI con i parametri corretti
Viene quindi chiesto su quanti processi si desidera eseguire l’algoritmo
Quanti Processi?
E quale tipo di algoritmo si vuole eseguire: inserire algo1 o algo2 oppure algo3
Quale algoritmo eseguire? [algo1 : algo2 : algo3]
Infine si deve premere invio quando si è pronti per eseguire l’ algoritmo
Enter per iniziare ->
70
Documentazione delle misure delle prestazioni
Le misure delle prestazioni vengono riassunte in file che vengono generati automaticamente da una
particolare funzione savePerformance presente nel file matrice.c.
Tali pagine vengono divise in due sezioni:
• Intestazione
• Corpo
Nell’intestazione, che è evidenziata da una cornice, vengono specificati il nome dell’algoritmo
utilizzato, il numero di processi, se la funzione di rallentamento è attivata oppure no e la dimensione
della matrice presa in considerazione.
Nel corpo invece viene specificata la lista dei processi con assegnato il tempo impiegato da tale
processo per effettuare le operazioni. Inoltre viene fornita una media del tempo impiegato tra tutti i
processi.
Qui sotto portiamo un esempio di pagina dove si è utilizzato l’algoritmo Processor Farm, 9 processi,
la funzione di rallentamento attivata a 40 cicli e con una matrice di dimensione 1920 × 1920.
71
###############################################################################
Algoritmo utilizzato:
Algoritmo Processor Farm
Numero di processi
9
Funzione di rallentamento attivata:
40 ms
Dimensione della matrici
1920
###############################################################################
Processo numero: 0
Tempo totale del root: 289.926316
Processo numero: 1
Tempo impiegato: 289.905706
Processo numero: 2
Tempo impiegato: 289.270183
Processo numero: 3
Tempo impiegato: 289.115312
Processo numero: 4
Tempo impiegato: 289.920823
Processo numero: 5
Tempo impiegato: 288.998096
Processo numero: 6
Tempo impiegato: 289.000793
Processo numero: 7
Tempo impiegato: 289.007451
Processo numero: 8
Tempo impiegato: 289.248978
Media tempo processi: 289.377073
72
Primitive MPI
In questa sezione vengono presentate le principali primitive MPI che sono state utilizzate nel codice
del progetto.
Primitive gestione ambiente MPI
MPI_Init( )
Inizializza l’esecuzione di un ambiente MPI. Questa funzione deve essere chiamata in ogni
programma MPI prima di qualsiasi funzione MPI e solo una volta. MPI_Init può essere utilizzata
per passare gli argomenti della linea di comando a tutti i processi.
MPI_Init (&argc,&argv)
MPI_Comm_size
Determina il numero di processi nel gruppo associato a un comunicatore. In generale viene
utilizzato con il comunicatore MPI_COMM_WORLD per determinare il numero di processi usati
nella propria applicazione.
MPI_Comm_size (comm,&size)
MPI_Comm_rank
Determina il rank dei processi all’interno di un comunicatore. Inizialmente ad ogni processo viene
assegnato un numero intero compreso tra 0 e il numero di processi -1 dentro il comunicatore
MPI_COMM_WORLD. Il valore del rank è unico per ogni processo nel comunicatore .
MPI_Comm_rank(comm,&rank)
MPI_Abort
Termina tutti i processi MPI associati al comunicatore.
MPI_Abort(comm,errorcode)
MPI_Get_Processor_Name
Ritorna il nome dei processori e la lunghezza dei nomi.
MPI_Get_processor_name(&name,&resultlength)
73
MPI_Wtime
Ritorna il tempo in secondi (in precisione double) di chiamata dei processori
MPI_Wtime()
MPI_Finalize
Termina l’esecuzione dell’ambiente MPI. Questa funzione deve essere presente in ogni programma
MPI.
MPI_Finalize()
Primitive comunicazione Point to Point
MPI_Send( )
Procedura che invia un buffer di memoria ad una particolare destinazione. Operazione di send
bloccante. La procedura ritorna il controllo solo quando il buffer dell’applicazione è libero per il
riutilizzo.
MPI_Send(buf, count, datatype, dest, tag, comm)
•
•
•
•
•
•
buf : Indirizzo del buffer di dati che devono essere spediti
count : numero di elementi di un certo tipo che devono essere inviati
datatype : tipo di dati predefiniti in MPI
dest : Rank del processo ricevente
tag : intero non negativo che identifica il messaggio
comm : contesto di comunicazione. Il comunicatore
MPI_COMM_WORLD.
predefinito
è
MPI_Recv( )
Procedura di ricezione di un buffer. Tale procedura è bloccante: ritorna il controllo solo quando
l’intero buffer è stato ricevuto.
MPI_Recv (buf, count, datatype, source, tag, com, status)
•
•
•
•
buf : Indirizzo del buffer dove verranno ricevuti i dati
count : numero di elementi di un certo tipo che devono essere ricevuti
datatype : tipo di dati predefiniti in MPI.
source : rank del processo sorgente
74
•
•
•
tag : intero non negativo che identifica il messaggio
comm : contesto di comunicazione. Il comunicatore
MPI_COMM_WORLD.
status: struttura dati che contiene la sorgente e il tag del messaggio.
predefinito
è
MPI_Sendrecv_replace
MPI_Sendrecv_replace esegue una send e una receive bloccante. Lo stesso buffer è utilizzato sia
per il send che per il receive così che il messaggio inviato è sostituito dal messaggio ricevuto.
MPI_Sendrecv_replace(buf,count,datatype,dest,sendtag,source,recvtag,comm,
status)
•
•
•
•
•
•
•
•
•
buf : indirizzo del buffer inviato e successivamente ricevuto
count : numero di elementi di un certo tipo che devono essere ricevuti
datatype : tipo del dato
dest : rank del processo ricevente
sendtag : intero non negativo che identifica il messaggio di invio
source : rank del processo sorgente
recvtag : intero non negativo che identifica il messaggio di ricezione
comm: comunicatore
status : struttura dati che contiene la sorgente e il tag del messaggio
Collettive di comunicazione
MPI_Barrier
Crea una barriera di sincronizzazione in un gruppo. Ogni processo quando ha raggiunto la barriera
si blocca finché tutti gli processi del comunicatore hanno raggiunto la barriera.
MPI_Barrier(Comm)
MPI_Bcast
Invio di un messaggio dal processo root a tutti gli altri processi presenti nel comunicatore.
MPI_Bcast (buf, count, datatype, root, comm)
•
•
•
•
•
buf : indirizzo del buffer inviato
count : numero di elementi di un certo tipo che devono essere ricevuti
datatype : tipo dei dati inviati
root : rank del processo sorgente
comm : comunicatore
75
MPI_Scatter
Distribuisce distinti messaggi da un singolo processo sorgente ad ogni processo presente nel
comunicatore
MPI_Scatter(sendbuf, sendcount,sendtype,recvbuf,revcount,recvtype,root,comm)
•
•
•
•
•
•
•
•
sendbuf: indirizzo del send buffer
sendcount: numero elementi inviati ad ogni processo
sendtype: tipo degli elementi presenti nel send buffer
recvbuf: indirizzo del receive buffer
recvcount: numero elementi nel receive buffer
recvtype: : tipo degli elementi presenti nel receive buffer
root: rank del processo che invia i dati
comm: comunicatore
MPI_Gather
Riunisce i messaggi distinti di ogni processo nel comunicatore in un singolo processo. Tale routine
è l’operazione inversa di MPI_Scatter.
MPI_Gather(sendbuf, sendcount,sendtype,recvbuf,revcount,recvtype,root,comm)
•
•
•
•
•
•
•
•
sendbuf: indirizzo iniziale del send buffer
sendcount: numero elementi nel send buffer
sendtype: tipo degli elementi presenti nel send buffer
recvbuf: indirizzo del receive buffer
recvcount: numero elementi per ogni singolo receive buffer
recvtype: : tipo degli elementi presenti nel receive buffer
root: rank del processo che raccoglie i dati
comm: comunicatore
MPI_Allgather
Concatenazione dei dati su tutti i processi presenti nel comunicatore. Ogni processo nel gruppo
effettua prima un’operazione di broadcasting del proprio dato a tutti i processi presenti nel gruppo,
poi effettua un’operazione MPI_Gather per concatenare tutti i dati.
MPI_Gather(sendbuf, sendcount,sendtype,recvbuf,revcount,recvtype,comm)
•
•
•
•
•
•
sendbuf: indirizzo iniziale del send buffer
sendcount: numero elementi nel send buffer
sendtype: tipo degli elementi presenti nel send buffer
recvbuf: indirizzo del receive buffer
recvcount: numero elementi ricevuti da ogni processo
recvtype: : tipo degli elementi presenti nel receive buffer
76
•
comm: comunicatore
Primitive per la gestione dei comunicatori
MPI_Comm_Split
Partiziona il comunicatore associato in sotto-comunicatori disgiunti tra loro.
MPI_Comm_Split(comm,color, key, newcomm)
•
•
•
•
comm.: comunicatore da partizionare
color: controllo dei sottoinsiemi assegnati
key: controllo dei rank assegnati
newcomm: nuovo comunicatore
Tipi Predefiniti in MPI
Nella tabella riassumiamo i principali tipi definiti in MPI confrontandoli con i tipi predefiniti del
linguaggio C.
Tipo dati MPI
MPI_CHAR
MPI_INT :
MPI_DOUBLE
MPI_LONG
MPI_SHORT
MPI_UNSIGNED_CHAR
MPI_UNSIGNED_SHORT
MPI_UNSIGNED
MPI_UNSIGNED_LONG
MPI_FLOAT
MPI_LONG_DOUBLE
Tipo dati C
signed char
signed int
double
signed long int
signed short int
unsigned char
unsigned short int
unsigned int
unsigned long int
float
Long double
77
Risultati test
Di seguito vengono riportati tutti i risultati dei test effettuati.
Per ogni algoritmo viene specificato la dimensione delle matrici quadrate A e B, il numero dei
processi sui quali si è eseguito l’algoritmo, il numero di cicli eseguiti dalla funzione di
rallentamento ed il tempo impiegato dal processo che ha accesso alle matrici A, B e C (processo con
rank 0).
Algoritmo Matrix Matrix
Dimensione
matrice
384
Ritardo
0
5
10
40
768
0
5
10
40
Numero processi Tempo medio
root
4
0.929744
9
0.814007
16
0.979227
4
1.175010
9
1.042601
16
0.872501
4
1.572951
9
1.019708
16
1.019708
4
3.034295
9
1.753060
16
1.456334
4
5.485505
9
3.988348
16
3.241951
4
8.131361
9
5.192860
16
4.293923
4
11.308421
9
7.691423
16
5.690817
4
23.677962
9
12.240944
16
11.187293
78
1152
0
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
18.897312
14.488862
9.523324
27.892869
14.418259
15.640759
38.955858
38.158344
18.195319
80.652735
39.478073
39.620218
98.851678
70.104640
45.202001
160.31033
122.023573
73.313982
325.469852
189.836414
97.380354
390.462323
173.413946
192.205315
Ritardo
Numero processi
Tempo medio
0
4
9
16
4
9
16
4
9
16
4
9
16
1.033331
0.845044
0.786103
1.323578
0.977513
0.923690
1.621842
1.072127
1.206426
3.210334
1.731938
1.354892
5
10
40
1920
0
5
10
40
Algoritmo Cannon
Dimensione
matrice
384
5
10
40
79
768
0
5
10
40
1152
0
5
10
40
1920
0
5
10
40
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
7.685075
4.166739
3.559943
8.963937
5.150870
4.271693
12.003956
22.007625
5.501585
25.334788
11.769905
8.183016
20.524266
11.854486
9.380467
28.261071
15.149448
11.750840
42.015027
34.130077
23.439799
84.131099
38.468620
33.750748
100.070636
69.150346
36.066406
162.561834
121.151701
64.537436
322.829983
215.558784
93.485949
398.580245
174.680116
186.814245
80
Algoritmo Processor Farm
Dimensione
matrice
384
Ritardo
Numero processi
Tempo medio
0
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
4
9
16
1.013475
0.790527
1.163005
1.833057
1.060500
0.945750
2.226456
1.221536
1.064294
3.991906
1.914332
1.781579
8.016242
3.711925
3.351492
12.444352
6.634593
4.914889
17.815742
8.692787
5.510070
31.391664
13.784691
8.992444
28.760064
14.044216
10.144463
46.567995
23.288855
15.677090
68.100770
28.066548
17.275973
109.154053
45.902152
29.362681
296.034891
121.558528
71.804350
557.816135
198.322609
110.366643
5
10
40
768
0
5
10
40
1152
0
5
10
40
1920
0
5
81
10
40
4
9
16
4
9
16
564.184569
247.105293
127.070426
743.951466
289.926316
168.583426
Algoritmo sequenziale
Dimensione
Matrice
384
768
1152
1920
Ritardo
Tempo
0
5
10
40
0
5
10
40
0
5
10
40
0
5
10
40
1.874726
3.141316
4.691039
12.002368
17.945671
26.697897
39.426890
92.198587
70.770948
122.784403
153.114975
328.350799
881.414489
1360.528847
1548.154627
2308.199205
82
Conclusione
Tramite gli algoritmi da noi implementati abbiamo potuto constatare che ilo comportamento degli
algoritmi Matrix Matrix e Cannon si equivalgono in termini di tempo, efficienza e speedup.
L’algoritmo Matrix Matrix soffre però di un forte sbilanciamento in presenza di in costo
computazionale elevato e quindi non applicabile a situazioni con tale carico.
In presenza di basso costo computazionale gli algoritmi Matrix Matrix e Cannon sono migliori a
quelle del Processor Farm che però riduce tale gap con l’aumento del numero computazioni e di
processi.
In generale quindi, in presenza di un problema computazionalmente elevato e quindi il caso in cui si
adottano gli algoritmi paralleli, l’algoritmo Processor Farm risulta essere il migliore sia in termini di
tempo che in termini di bilanciamento del carico.
Nella lettura dei risultati deve essere tenuto in considerazione il fatto che si è utilizzato un cluster le
cui prestazioni possono essere state “influenzate” dal carico variabile della rete.
83
Appendice
Grafici numero processori – tempo
Matrice 384
Matrice 384 − Funzione rallentamento attivata a 5 cicli
2
Matrix Multiply
Cannon Algorithm
Processor Farm
1.8
1.6
1.4
Tempo (sec)
1.2
1
0.8
0.6
0.4
0.2
0
4
6
8
10
Numero Processi (np)
12
14
16
84
Matrice 384 − Funzione rallentamento attivata a 10 cicli
3
Matrix Multiply
Cannon Algorithm
Processor Farm
2.5
Tempo (sec)
2
1.5
1
0.5
0
4
6
8
10
Numero Processi (np)
12
14
16
Matrice 384 − Funzione rallentamento attivata a 40 cicli
5
Matrix Multiply
Cannon Algorithm
Processor Farm
4.5
4
3.5
Tempo (sec)
3
2.5
2
1.5
1
0.5
0
4
6
8
10
Numero Processi (np)
12
14
16
85
Matrice 768
Matrice 768 − Funzione rallentamemto disattivata
9
Matrix Multiply
Cannon Algorithm
Processor Farm
8
7
Tempo (sec)
6
5
4
3
2
1
0
4
6
8
10
12
Numero Processi (np)
14
16
Matrice 768 − Funzione rallentamemto attivata a 5 cicli
13
Matrix Multiply
Cannon Algorithm
Processor Farm
12
11
Tempo (sec)
10
9
8
7
6
5
4
3
4
6
8
10
12
Numero Processi (np)
14
16
86
Matrice 768 − Funzione rallentamemto attivata a 10 cicli
18
Matrix Multiply
Cannon Algorithm
Processor Farm
16
Tempo (sec)
14
12
10
8
6
4
4
6
8
10
12
Numero Processi (np)
14
16
Matrice 768 − Funzione rallentamemto attivata a 40 cicli
Matrix Multiply
Cannon Algorithm
Processor Farm
30
Tempo (sec)
25
20
15
10
4
6
8
10
12
Numero Processi (np)
14
16
87
Matrice 1152
Matrice 1152 − Funzione rallentamento disattivata
40
Matrix Multiply
Cannon Algorithm
Processor Farm
35
30
Tempo (sec)
25
20
15
10
5
0
4
6
8
10
Numero Processi (np)
12
14
16
Matrice 1152 − Funzione rallentamento attivata a 5 cicli
50
Matrix Multiply
Cannon Algorithm
Processor Farm
45
40
35
Tempo (sec)
30
25
20
15
10
5
0
4
6
8
10
Numero Processi (np)
12
14
16
88
Matrice 1152 − Funzione rallentamento attivata 10 cicli
Matrix Multiply
Cannon Algorithm
Processor Farm
70
60
Tempo (sec)
50
40
30
20
10
0
4
6
8
10
Numero Processi (np)
12
14
16
Matrice 1152 − Funzione rallentamento attivata 40 cicli
120
Matrix Multiply
Cannon Algorithm
Processor Farm
110
100
90
Tempo (sec)
80
70
60
50
40
30
20
4
6
8
10
Numero Processi (np)
12
14
16
89
Matrice 1920
Matrice 1920 − Funzione rallentamento disattivata
300
Matrix Multiply
Cannon Algorithm
Processor Farm
250
Tempo (sec)
200
150
100
50
0
4
6
8
10
12
Numero Processi (np)
14
16
Matrice 1920 − Funzione rallentamemto attivata a 5 cicli
600
Matrix Multiply
Cannon Algorithm
Processor Farm
500
Tempo (sec)
400
300
200
100
0
4
6
8
10
12
Numero Processi (np)
14
16
Matrice 1920 − Funzione rallentamemto attivata a 10 cicli
600
Matrix Multiply
Cannon Algorithm
Processor Farm
500
Tempo (sec)
400
300
200
100
0
4
6
8
10
12
Numero Processi (np)
14
16
90
Matrice 1920 − Funzione rallentamemto attivata a 40 cicli
Matrix Multiply
Cannon Algorithm
Processor Farm
700
600
Tempo (sec)
500
400
300
200
100
0
4
6
8
10
12
Numero Processi (np)
14
16
Grafici ritardo – tempo
Matrice 384
Matrice 384 − Numero Processi 4
5
Matrix Multiply
Cannon Algorithm
Processor Farm
4.5
4
3.5
Tempo (sec)
3
2.5
2
1.5
1
0.5
0
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
91
Matrice 384 − Numero Processi 9
Matrix Multiply
Cannon Algorithm
Processor Farm
2
Tempo (sec)
1.5
1
0.5
0
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
Matrice 384 − Numero Processi 16
2.5
Matrix Multiply
Cannon Algorithm
Processor Farm
2
Tempo (sec)
1.5
1
0.5
0
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
92
Matrice 768
Matrice 768 − Numero processi 9
14
Tempo (sec)
12
10
8
Matrix Multiply
Cannon Algorithm
6
Processor Farm
4
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
30
35
40
Matrice 768 − Numero processi 16
12
11
Matrix Multiply
Cannon Algorithm
10
Processor Farm
Tempo (sec)
9
8
7
6
5
4
3
0
5
10
15
20
25
Ritardo (numero cicli)
93
Matrice 1152
Matrice 1152 − Numero Processi 4
150
Matrix Multiply
Cannon Algorithm
Processor Farm
Tempo (sec)
100
50
0
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
Matrice 1152 − Numero Processi 9
80
Matrix Multiply
Cannon Algorithm
Processor Farm
70
60
Tempo (sec)
50
40
30
20
10
0
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
94
Matrice 1152 − Numero Processi 16
50
Matrix Multiply
Cannon Algorithm
Processor Farm
45
40
35
Tempo (sec)
30
25
20
15
10
5
0
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
Matrice 1920
Matrice 1920 − Numero processi 4
700
Tempo (sec)
600
500
400
300
Matrix Multiply
Cannon Algorithm
200
Processor Farm
100
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
95
Matrice 1920 − Numero processi 9
300
Tempo (sec)
250
200
150
Matrix Multiply
Cannon Algorithm
Processor Farm
100
0
5
10
15
20
25
Ritardo (numero cicli)
30
35
40
30
35
40
Matrice 1920 − Numero processi 16
200
Matrix Multiply
180
Cannon Algorithm
Processor Farm
160
Tempo (sec)
140
120
100
80
60
40
20
0
0
5
10
15
20
25
Ritardo (numero cicli)
96
Grafici dimensione matrice – tempo
Funzione di rallentamento disattivata
Funzione rallentamento disattivata − numero processi 4
300
Matrix Multiply
250
Cannon Algorithm
Processor Farm
Tempo (sec)
200
150
100
50
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
1800
2000
Funzione rallentamento disattivata − numero processi 9
120
Matrix Multiply
Cannon Algorithm
100
Processor Farm
Tempo (sec)
80
60
40
20
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
97
Funzione rallentamento disattivata − numero processi 16
70
Matrix Multiply
Cannon Algorithm
60
Processor Farm
Tempo (sec)
50
40
30
20
10
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Funzione di rallentamento attivata : 5 cicli
Funzione rallentamento attivata a 5 cicli − numero processi 4
450
Matrix Multiply
400
Cannon Algorithm
Tempo (sec)
350
Processor Farm
300
250
200
150
100
50
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Funzione rallentamento attivata a 5 cicli − numero processi 9
200
180
Matrix Multiply
160
Cannon Algorithm
Processor Farm
Tempo (sec)
140
120
100
80
60
40
20
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
98
Funzione rallentamento attivata a 5 cicli − numero processi 16
100
Matrix Multiply
Cannon Algorithm
Processor Farm
Tempo (sec)
80
60
40
20
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Funzione di rallentamento attivata : 10 cicli
Funzione rallentamento attivata a 10 cicli − numero processi 4
500
Matrix Multiply
Cannon Algorithm
Processor Farm
Tempo (sec)
400
300
200
100
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Funzione rallentamento attivata a 10 cicli − numero processi 9
250
Matrix Multiply
Cannon Algorithm
Tempo (sec)
200
Processor Farm
150
100
50
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
99
Funzione rallentamento attivata a 10 cicli − numero processi 16
120
Matrix Multiply
Cannon Algorithm
Tempo (sec)
100
Processor Farm
80
60
40
20
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Funzione di rallentamento attivata : 40 cicli
Funzione rallentamento attivata a 40 cicli − numero processi 4
700
Matrix Multiply
600
Cannon Algorithm
Processor Farm
Tempo (sec)
500
400
300
200
100
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Funzione rallentamento attivata a 40 cicli − numero processi 9
Matrix Multiply
Cannon Algorithm
250
Processor Farm
Tempo (sec)
200
150
100
50
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
100
Funzione rallentamento attivata a 40 cicli − numero processi 16
200
180
Matrix Multiply
Cannon Algorithm
160
Processor Farm
Tempo (sec)
140
120
100
80
60
40
20
0
400
600
800
1000
1200
1400
Dimensione matrice
1600
1800
2000
Grafici numero processi – speedup
Matrice 384
Calcolo Speedup − Funzione disattivata − Matrice: 384 X 384
2.5
Speedup (Sp(n))
2
Matrix Multiply
Cannon Algorithm
1.5
1
Processor Farm
4
6
8
10
12
Numero Processori (np)
14
16
101
Calcolo Speedup − Funzione attivata 5 cicli − Matrice: 384 X 384
4
3.5
Speedup (Sp(n))
3
2.5
Matrix Multiply
Cannon Algorithm
Processor Farm
2
1.5
1
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Speedup − Funzione attivata 10 cicli − Matrice: 384 X 384
6
5.5
5
Speedup (Sp(n))
4.5
4
3.5
3
Matrix Multiply
Cannon Algorithm
2.5
Processor Farm
2
1.5
1
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Speedup − Funzione attivata 40 cicli − Matrice: 384 X 384
10
9
8
Speedup (Sp(n))
7
6
5
Matrix Multiply
Cannon Algorithm
4
Processor Farm
3
2
1
4
6
8
10
12
Numero Processori (np)
14
16
102
Matrice 768
Calcolo Speedup − Funzione disattivata − Matrice: 768 X 768
6
5
Speedup (Sp(n))
4
Matrix Multiply
3
Cannon Algorithm
Processor Farm
2
1
0
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Speedup − Funzione attivata 5 cicli − Matrice: 768 X 768
7
6
Speedup (Sp(n))
5
4
Matrix Multiply
3
Cannon Algorithm
Processor Farm
2
1
0
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Speedup − Funzione attivata 10 cicli − Matrice: 768 X 768
7
6
Speedup (Sp(n))
5
4
Matrix Multiply
3
Cannon Algorithm
Processor Farm
2
1
0
4
6
8
10
12
Numero Processori (np)
14
16
103
Calcolo Speedup − Funzione attivata 40 cicli − Matrice: 768 X 768
12
10
Speedup (Sp(n))
8
6
Matrix Multiply
Cannon Algorithm
4
Processor Farm
2
0
4
6
8
10
12
Numero Processori (np)
14
16
Matrice 1152
Calcolo Speedup − Funzione F disattivata − Matrice: 1152 X 1152
12
Matrix Multiply
Cannon Algorithm
Processor Farm
11
10
Speedup (sp(n))
9
8
7
6
5
4
3
2
1
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Speedup − Funzione F attivata : 5 − Matrice: 1152 X 1152
12
Matrix Multiply
11
Cannon Algorithm
10
Processor Farm
Speedup (sp(n))
9
8
7
6
5
4
3
2
1
4
6
8
10
12
Numero Processori (np)
14
16
104
Calcolo Speedup − Funzione F attivata : 10 − Matrice: 1152 X 1152
12
Matrix Multiply
Cannon Algorithm
Processor Farm
11
10
Speedup (sp(n))
9
8
7
6
5
4
3
2
1
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Speedup − Funzione F attivata : 40 − Matrice: 1152 X 1152
12
11
10
Speedup (sp(n))
9
8
7
6
5
Matrix Multiply
4
Cannon Algorithm
Processor Farm
3
2
1
4
6
8
10
12
Numero Processori (np)
14
16
Matrice 1920
Calcolo Speedup − Funzione F disattivata − Matrice: 1920 X 1920
25
Matrix Multiply
Cannon Algorithm
Speedup (sp(n))
20
Processor Farm
15
10
5
2
4
6
8
10
Numero Processori (np)
12
14
16
105
Calcolo Speedup − Funzione F attivata : 5 − Matrice: 1920 X 1920
25
Matrix Multiply
Cannon Algorithm
Speedup (sp(n))
20
Processor Farm
15
10
5
2
4
6
8
10
Numero Processori (np)
12
14
16
Calcolo Speedup − Funzione F attivata : 10 − Matrice: 1920 X 1920
25
Matrix Multiply
Cannon Algorithm
Processor Farm
Speedup (sp(n))
20
15
10
5
4
6
8
10
12
Numero Processori (np)
14
16
106
Grafici numero processori – efficienza
Matrice 384
Calcolo Efficenza − Funzione F disattivata : − Matrice: 384 X 384
1
Matrix Multiply
Cannon Algorithm
Processor Farm
0.9
0.8
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
Numero Processori (np)
12
14
16
Calcolo Efficenza − Funzione F attivata : 5 − Matrice: 384 X 384
1
Matrix Multiply
Cannon Algorithm
Processor Farm
0.9
0.8
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
Numero Processori (np)
12
14
16
107
Calcolo Efficenza − Funzione F attivata : 10 − Matrice: 384 X 384
1
Matrix Multiply
Cannon Algorithm
Processor Farm
0.9
0.8
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
Numero Processori (np)
12
14
16
Calcolo Efficenza − Funzione F attivata : 40 − Matrice: 384 X 384
1
Matrix Multiply
Cannon Algorithm
Processor Farm
0.9
0.8
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
Numero Processori (np)
12
14
16
108
Matrice 768
Calcolo Efficienza − Funzione disattivata − Matrice: 768 X 768
1
0.9
Matrix Multiply
Cannon Algorithm
0.8
Processor Farm
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Efficienza − Funzione attivata 5 cicli − Matrice: 768 X 768
1
Matrix Multiply
Cannon Algorithm
Processor Farm
0.9
0.8
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Efficienza − Funzione attivata 10 cicli − Matrice: 768 X 768
1
0.9
Matrix Multiply
0.8
Cannon Algorithm
Processor Farm
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
12
Numero Processori (np)
14
16
109
Calcolo Efficienza − Funzione attivata 40 cicli − Matrice: 768 X 768
1
0.9
Efficenza (E(n))
0.8
0.7
Matrix Multiply
0.6
Cannon Algorithm
Processor Farm
0.5
0.4
0.3
4
6
8
10
12
Numero Processori (np)
14
16
Matrice 1152
Calcolo Efficenza − Funzione F disattivata Matrice: 1152 X 1152
1
Matrix Multiply
Cannon Algorithm
Processor Farm
0.9
0.8
Efficenza (E(n))
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
4
6
8
10
Numero Processori (np)
12
14
16
110
Calcolo Efficenza − Funzione F attivata : 5 − Matrice: 1152 X 1152
1.4
Matrix Multiply
Cannon Algorithm
Processor Farm
1.2
Efficenza (E(n))
1
0.8
0.6
0.4
0.2
0
4
6
8
10
Numero Processori (np)
12
14
16
Calcolo Efficenza − Funzione F attivata : 5 − Matrice: 1152 X 1152
1.4
Matrix Multiply
Cannon Algorithm
Processor Farm
1.2
Efficenza (E(n))
1
0.8
0.6
0.4
0.2
0
4
6
8
10
Numero Processori (np)
12
14
16
111
Calcolo Efficenza − Funzione F attivata : 5 − Matrice: 1152 X 1152
1.4
Matrix Multiply
Cannon Algorithm
Processor Farm
1.2
Efficenza (E(n))
1
0.8
0.6
0.4
0.2
0
4
6
8
10
Numero Processori (np)
12
14
16
Matrice 1920
Calcolo Efficenza − Funzione F disattivata − Matrice: 1920 X 1920
2.5
Matrix Multiply
Cannon Algorithm
Efficenza (E(n))
2
Processor Farm
1.5
1
0.5
0
4
6
8
10
12
Numero Processori (np)
14
16
112
Calcolo Efficienza − Funzione arrivata 5 cicli − Matrice: 1920 X 1920
2.5
Matrix Multiply
Cannon Algorithm
Processor Farm
Efficenza (E(n))
2
1.5
1
0.5
0
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Efficienza − Funzione arrivata 10 cicli − Matrice: 1920 X 1920
2.5
Matrix Multiply
2
Cannon Algorithm
Efficenza (E(n))
Processor Farm
1.5
1
0.5
0
4
6
8
10
12
Numero Processori (np)
14
16
Calcolo Efficienza − Funzione arrivata 40 cicli − Matrice: 1920 X 1920
2
1.8
Matrix Multiply
Cannon Algorithm
1.6
Processor Farm
Efficenza (E(n))
1.4
1.2
1
0.8
0.6
0.4
0.2
0
4
6
8
10
12
Numero Processori (np)
14
16
113
Grafici bilanciamento del carico
Matrice 384
Matrice 384 − Funzione rallentamento disattivata − Numero processi = 4
1.2
1.1
Tempo (sec)
1
0.9
0.8
Matrix Multiply
Cannon Algorithm
Processor Farm
0.7
0.6
0.5
0
0.5
1
1.5
Rank processo
2
2.5
3
Matrice 384 − Funzione rallentamento 40 cicli − Numero processi = 4
4.5
Matrix Multiply
Cannon Algorithm
Processor Farm
Tempo (sec)
4
3.5
3
2.5
0
0.5
1
1.5
Rank processo
2
2.5
3
Matrice 384 − Funzione rallentamento disattivata − Numero processi = 16
1.4
Matrix Multiply
Cannon Algorithm
Processor Farm
1.3
1.2
Tempo (sec)
1.1
1
0.9
0.8
0.7
0.6
0.5
0.4
0
5
10
15
Rank processo
114
Matrice 384 − Funzione rallentamento 40 cicli − Numero processi = 16
2
Matrix Multiply
Cannon Algorithm
Processor Farm
1.9
1.8
Tempo (sec)
1.7
1.6
1.5
1.4
1.3
1.2
1.1
1
0
5
10
15
Rank processo
Matrice 1920
Matrice 1920 − Funzione rallentamento disattivata − Numero processi = 4
300
Tempo (sec)
250
Matrix Multiply
Cannon Algorithm
Processor Farm
200
150
100
0
0.5
1
1.5
Rank processo
2
2.5
3
Matrice 1920 − Funzione rallentamento 40 cicli − Numero processi = 4
750
700
Matrix Multiply
Cannon Algorithm
Processor Farm
Tempo (sec)
650
600
550
500
450
400
0
0.5
1
1.5
Rank processo
2
2.5
3
115
Matrice 1920 − Funzione rallentamento disattivata − Numero processi = 16
80
70
Tempo (sec)
60
Matrix Multiply
Cannon Algorithm
Processor Farm
50
40
30
20
0
5
10
15
Rank processo
Matrice 1920 − Funzione rallentamento 40 cicli − Numero processi = 16
200
180
Tempo (sec)
160
Matrix Multiply
Cannon Algorithm
Processor Farm
140
120
100
80
0
5
10
15
Rank processo
116