Corso di Architettura dei calcolatori e parallelismo
anno 2007/2008
Esericizi
Marco Trentini matricola 062275
Caratteristiche del Cluster:
SCILX: beowulf cluster of Grandi Attrezzature Consortium
20 dual AMD Athlon MP 2000+ SMP nodes (totally 40 CPUs)
1 GByte RAM per node (totally 20 GBytes)
NAS Dell Powervault 725N with about 300 GByte of storage
10/100 MBit Ethernet service network
Dolphin Interconnect Solutions D334/D335 for High Performance Computing (v3.3.0.2)
Operative System: Linux Debian Lenny
Compilers: GCC-4.x, GCC-3.x, GFORTRAN, G77-3.x, PGI 7.0 (require license)
Shells/Interpreters: BASH, KSH, TCSH, PERL, JAVA-1.6, PYTHON-2.4, PYTHON-2.5
Softwares: MP-MPICH-1.5.0 , OPENMPI-1.2.6 , NMPI-1.3.1
Libraries: ACML-3.6.1 (GNU, GFORTRAN, PGI), FFT3, FFTW, BLACS, BLAS,
SCALAPACK, GSL
Support to OpenMP
Esercizio 1: utilizzo di sistemi a code di famiglia Torque/PBS. (20 min)
Tutti gli esercizi seguenti richiederanno le conoscenze acquisite in questo esercizio per poter
eseguire i propri programmi sul cluster in adozione.
(a) Sottomettete al cluster scilx il comando “uname –a” preparando un opportuno script di
sottomissione. Monitorare l’attività del proprio processo.
(b) Si provino a modificare i parametri dello script di sottomissione valutando gli effetti
ottenuti.
#!/bin/bash
#PBS -V
#PBS -k eo
#PBS -N HW_openmpi
#PBS -l nodes=10:ppn=1
#PBS -l walltime=00:30:00
echo "Start @" `date`
echo "The nodefile is ${PBS_NODEFILE} and it contains:"
cat ${PBS_NODEFILE}
echo ""
source /usr/local/Modules/3.2.5/init/bash
module load OPENMPI-1.2.6-GNU
cd $PBS_O_WORKDIR
NUM_NODES=`cat $PBS_NODEFILE | wc -l`
#mpdboot -n ${NUM_NODES} -f ${PBS_NODEFILE} -r rsh -1
mpirun -np ${NUM_NODES} uname -a
#mpirun -np ${NUM_NODES} ./first_mpi
#mpirun -np ${NUM_NODES} ./trapezoid_mpi_sync
#mpirun -np ${NUM_NODES} ./trapezoid_mpi_async
#mpirun -np ${NUM_NODES} ./trapezoid_mpi_master
#
#mpdallexit
echo "End @" `date`
exit 0
esempio di output:
studentipar4@scilx:~/ex$ qsub test_mpi_eth.sh
10711.scilx.disco.unimib.it
studentipar4@scilx:~$ cat HW_openmpi.o10711
Start @ Sat May 31 11:14:28 CEST 2008
The nodefile is /var/spool/torque/aux//10711.scilx.disco.unimib.it and it contains:
node08
node06
node19
node18
node10
node17
node04
node02
node01
node03
Linux node08 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
Linux node03 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
Linux node01 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
Linux node02 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
Linux node10 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
Linux node04 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
Linux node18 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
Linux node17 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
Linux node19 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
Linux node06 2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008 i686 GNU/Linux
End @ Sat May 31 11:14:28 CEST 2008
Esercizio 2: Hello World (40 min)
Scrivere un semplice programma in linguaggio C che faccia si che ogni nodo della
computazione stampi il messaggio “Hi, I’m node X on Y” dove X è l’identificativo del nodo
mentre Y è il numero totale di processi coinvolti nella computazione.
#include "mpi.h"
#include <stdio.h>
int main(int argc,char *argv[]){
int numtasks, rank, rc, resultlen;
char name[100];
double time;
rc = MPI_Init(&argc,&argv);
if (rc != MPI_SUCCESS) {
printf ("Error starting MPI program. Terminating.\n");
MPI_Abort(MPI_COMM_WORLD, rc);
}
MPI_Comm_size(MPI_COMM_WORLD,&numtasks);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Get_processor_name(name,&resultlen);
time=MPI_Wtime();
printf ("Number of tasks=%d My rank=%d on %s\n", numtasks,rank,name);
printf ("Time for task=%d : %fl\n",rank,time);
sleep(2);
time=MPI_Wtime();
printf ("Time for task=%d afer about 2 second : %fl\n",rank,time);
/******* do some work *******/
MPI_Finalize();
}
esempio di output:
studentipar4@scilx:~$ cat HW_openmpi.o10712
Start @ Sat May 31 11:17:14 CEST 2008
The nodefile is /var/spool/torque/aux//10712.scilx.disco.unimib.it and it contains:
node08
node06
node19
node18
node10
node17
node04
node02
node01
node03
Number of tasks=10 My rank=1 on node06
Time for task=1 : 1212225434.511091l
Number of tasks=10 My rank=2 on node19
Time for task=2 : 1212225434.992027l
Number of tasks=10 My rank=6 on node04
Time for task=6 : 1212225434.564736l
Number of tasks=10 My rank=5 on node17
Time for task=5 : 1212225434.659058l
Number of tasks=10 My rank=7 on node02
Number of tasks=10 My rank=9 on node03
Time for task=9 : 1212225434.569362l
Number of tasks=10 My rank=0 on node08
Number of tasks=10 My rank=4 on node10
Time for task=4 : 1212225434.502109l
Number of tasks=10 My rank=3 on node18
Time for task=3 : 1212225435.181055l
Number of tasks=10 My rank=8 on node01
Time for task=8 : 1212225434.862988l
Time for task=7 : 1212225434.775596l
Time for task=0 : 1212225434.719827l
Time for task=1 afer about 2 second : 1212225436.511226l
Time for task=2 afer about 2 second : 1212225436.992183l
Time for task=6 afer about 2 second : 1212225436.564882l
Time for task=5 afer about 2 second : 1212225436.659205l
Time for task=7 afer about 2 second : 1212225436.775974l
Time for task=9 afer about 2 second : 1212225436.569501l
Time for task=0 afer about 2 second : 1212225436.720140l
Time for task=4 afer about 2 second : 1212225436.502250l
Time for task=3 afer about 2 second : 1212225437.181198l
Time for task=8 afer about 2 second : 1212225436.863137l
End @ Sat May 31 11:17:17 CEST 2008
Esercizio 3: Integrazione di funzioni tramite il metodo dei trapezi. (1h)
Il programma tapezoid.c contiene un’implementazione del metodo dei trapezi generalizzato: un
semplice metodo che permette di calcolare il valore dell’integrale di una funzione in un dato
intervallo.
(a) Si parallelizzi il codice in questione utilizzando comunicazioni punto a punto per accumulare
le porzioni di calcolo valutate dalle partizioni del dominio della funzione. (e.g. s_node0>s_node1->...->s_nodeN->s_node0->print risultato). Valutate lo speedup ed isoefficienza
dell’implementazione parallela rispetto alla versione sequenziale del codice. Misurare le
tempistiche di programma utilizzando le apposite chiamate MPI.
(b) Modificate l’implementazione parallela in modo da sovrapporre i tempi di calcolo a quelli di
comunicazioni per la propagazione dei risultati parziali. Utilizzate le chiamate MPI_isend,
MPI_irecv. Come variano le performance rispetto al punto (a)?
Versione Seriale
#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#include <time.h>
typedef double precision;
#define START 1
#define END 4
#define NBROFTRAP 10000000000ULL
/*#define FUNCTION sin*/
#define FUNCTION myfunc2
precision myfunc1( precision x){
return x;
}
precision myfunc2( precision x){
return 3;
}
precision trapezoid( precision a, precision b, precision h, unsigned int iter){
precision sum;
precision x;
unsigned int i;
sum=0;
x=a;
for(i=0;i<iter-1;i++){
sum=sum+ ( 0.5 * h * ( FUNCTION(x) + FUNCTION(x+h)));
x=x+h;
//printf("partial sum of %d trapezoid=%lf \n",i+1,sum);
}
sum=sum+ ( 0.5 * h * ( FUNCTION(x) + FUNCTION(b)));
return sum;
}
precision a;
precision h;
precision b;
precision n;
struct timeval start,end;
int main(int argc,char *argv[]){
gettimeofday(&start,NULL);
#if 0
printf("Enter value for a: ");
scanf("%lf", &a);
printf("Enter value for b: ");
scanf("%lf", &b);
printf("Enter number of rectangles: ");
scanf("%d", &n);
#endif
a=START;
b=END;
n=NBROFTRAP;
h=(b-a)/n;
#if 0
x=a;
for(i=0;i<n;i++){
f=FUNCTION(x+(h*i));
printf("x=%lf f(x)=%lf \n",x+(h*i),f);
}
#endif
printf("The value of the integral is: %lf\n", trapezoid(a,b,h,n));
gettimeofday(&end,NULL);
end.tv_sec=end.tv_sec-start.tv_sec;
end.tv_usec=end.tv_usec-start.tv_usec;
if(end.tv_usec<0){
end.tv_usec=end.tv_usec+1000000;
end.tv_sec--;
}
printf("Time spent calculating integral : %d sec %d usec\n",end.tv_sec,end.tv_usec);
return 0;
}
Versione (a)
#include "mpi.h"
#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#include <time.h>
typedef double precision;
/*#define FUNCTION sin*/
#define FUNCTION myfunc2
precision myfunc1( precision x){
return x;
}
precision myfunc2( precision x){
return 3;
}
precision trapezoid( precision a, precision b, precision h, unsigned int iter){
precision sum;
precision x;
unsigned int i;
sum=0;
x=a;
for(i=0;i<iter-1;i++){
sum=sum+ ( 0.5 * h * ( FUNCTION(x) + FUNCTION(x+h)));
x=x+h;
//printf("partial sum of %d trapezoid=%lf \n",i+1,sum);
}
sum=sum+ ( 0.5 * h * ( FUNCTION(x) + FUNCTION(b)));
return sum;
}
check_mpi(int rc){
if (rc != MPI_SUCCESS) {
printf ("Error starting MPI program. Terminating.\n");
MPI_Abort(MPI_COMM_WORLD, rc);
}
}
#define START 1
#define END 4
#define NBROFTRAP 1000000000ULL
int main(int argc,char *argv[]){
int numtasks, rank, rc, resultlen;
char name[100];
double time_start, time_end;
precision a=START;
precision b=END;
precision h;
precision sum;
precision sum_app;
double n=NBROFTRAP;
unsigned int iter;
int dest, source, tag , count;
MPI_Status Stat;
struct timeval start,end;
gettimeofday(&start,NULL);
h=(b-a)/n;
sum=0;
time_start=MPI_Wtime();
printf ("Time start for task=%d : %fl\n",rank,time_start);
rc = MPI_Init(&argc,&argv);
check_mpi(rc);
MPI_Comm_size(MPI_COMM_WORLD,&numtasks);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Get_processor_name(name,&resultlen);
printf ("Number of tasks=%d My rank=%d on %s\n", numtasks,rank,name);
iter=n/numtasks;
a=a+(rank*iter*h);
if(rank==numtasks-1)
iter=n-(iter*(numtasks-1));
else
b=a+(iter*h);
printf("task=%d a=%lf b=%lf h=%lf iter=%d\n",rank,a,b,h,iter);
sum=trapezoid(a,b,h,iter);
printf("task=%d sum=%lf\n",rank,sum);
if(rank==0){
dest=1;
tag=1;
source=numtasks-1;
rc = MPI_Send(&sum, 1, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD);
check_mpi(rc);
rc = MPI_Recv(&sum, 1, MPI_DOUBLE, source, tag, MPI_COMM_WORLD, &Stat);
check_mpi(rc);
rc = MPI_Get_count(&Stat, MPI_DOUBLE, &count);
printf("Task %d: Received %d double(s) from task %d with tag %d \n",
rank, count, Stat.MPI_SOURCE, Stat.MPI_TAG);
printf("The value of the integral is: %lf\n", sum);
}
else{
if(rank==numtasks-1)
dest=0;
else
dest=rank+1;
tag=1;
source=rank-1;
rc = MPI_Recv(&sum_app, 1, MPI_DOUBLE, source, tag, MPI_COMM_WORLD, &Stat);
check_mpi(rc);
rc = MPI_Get_count(&Stat, MPI_DOUBLE, &count);
printf("Task %d: Received %d double(s) from task %d with tag %d \n",
rank, count, Stat.MPI_SOURCE, Stat.MPI_TAG);
sum=sum+sum_app;
printf("The value of sum piled up is: %lf\n", sum);
rc = MPI_Send(&sum, 1, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD);
check_mpi(rc);
}
time_end=MPI_Wtime();
printf ("Time end for task=%d : %fl\n",rank,time_start);
printf ("Time for task=%d : %fl\n",rank,time_end-time_start);
MPI_Finalize();
gettimeofday(&end,NULL);
end.tv_sec=end.tv_sec-start.tv_sec;
end.tv_usec=end.tv_usec-start.tv_usec;
if(end.tv_usec<0){
end.tv_usec=end.tv_usec+1000000;
end.tv_sec--;
}
printf("gettimeofday for task=%d , %d sec %d usec\n",rank,end.tv_sec,end.tv_usec);
}
Versione (b)
#include "mpi.h"
#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#include <time.h>
typedef double precision;
/*#define FUNCTION sin*/
#define FUNCTION myfunc2
precision myfunc1( precision x){
return x;
}
precision myfunc2( precision x){
return 3;
}
precision trapezoid( precision a, precision b, precision h, unsigned int iter){
precision sum;
precision x;
unsigned int i;
sum=0;
x=a;
for(i=0;i<iter-1;i++){
sum=sum+ ( 0.5 * h * ( FUNCTION(x) + FUNCTION(x+h)));
x=x+h;
//printf("partial sum of %d trapezoid=%lf \n",i+1,sum);
}
sum=sum+ ( 0.5 * h * ( FUNCTION(x) + FUNCTION(b)));
return sum;
}
check_mpi(int rc){
if (rc != MPI_SUCCESS) {
printf ("Error starting MPI program. Terminating.\n");
MPI_Abort(MPI_COMM_WORLD, rc);
}
}
#define START 1
#define END 4
#define NBROFTRAP 1000000000ULL
int main(int argc,char *argv[]){
int numtasks, rank, rc, resultlen;
char name[100];
double time_start, time_end;
precision a=START;
precision b=END;
precision h;
precision sum;
precision sum_rec;
precision sum_send;
unsigned int n=NBROFTRAP;
unsigned int iter;
int dest, source, tag , count;
MPI_Status Stat;
MPI_Request reqs[2];
struct timeval start,end;
gettimeofday(&start,NULL);
h=(b-a)/n;
sum=0;
time_start=MPI_Wtime();
printf ("Time start for task=%d : %fl\n",rank,time_start);
rc = MPI_Init(&argc,&argv);
check_mpi(rc);
MPI_Comm_size(MPI_COMM_WORLD,&numtasks);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Get_processor_name(name,&resultlen);
printf ("Number of tasks=%d My rank=%d on %s\n", numtasks,rank,name);
iter=n/numtasks;
a=a+(rank*iter*h);
if(rank==numtasks-1)
iter=n-(iter*(numtasks-1));
else
b=a+(iter*h);
if(rank==0){
dest=1;
tag=1;
source=numtasks-1;
}
else{
if(rank==numtasks-1)
dest=0;
else
dest=rank+1;
tag=1;
source=rank-1;
}
rc = MPI_Irecv(&sum_rec, 1, MPI_DOUBLE, source, tag, MPI_COMM_WORLD, &reqs[0]);
check_mpi(rc);
printf("task=%d a=%lf b=%lf h=%lf iter=%d\n",rank,a,b,h,iter);
sum=trapezoid(a,b,h,iter);
printf("task=%d sum=%lf\n",rank,sum);
if(rank==0)
sum_send=sum;
else{
MPI_Wait(&reqs[0],&Stat);
rc = MPI_Get_count(&Stat, MPI_DOUBLE, &count);
printf("Task %d: Received %d double(s) from task %d with tag %d \n",
rank, count, Stat.MPI_SOURCE, Stat.MPI_TAG);
sum_send=sum+sum_rec;
printf("The value of sum piled up is: %lf\n", sum_send);
}
rc = MPI_Isend(&sum_send, 1, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD,&reqs[1]);
check_mpi(rc);
if(rank==0){
MPI_Wait(&reqs[0],&Stat);
rc = MPI_Get_count(&Stat, MPI_DOUBLE, &count);
printf("Task %d: Received %d double(s) from task %d with tag %d \n",
rank, count, Stat.MPI_SOURCE, Stat.MPI_TAG);
printf("The value of the integral is: %lf\n", sum_rec);
}
time_end=MPI_Wtime();
printf ("Time end for task=%d : %fl\n",rank,time_start);
printf ("Time for task=%d : %fl\n",rank,time_end-time_start);
MPI_Finalize();
gettimeofday(&end,NULL);
end.tv_sec=end.tv_sec-start.tv_sec;
end.tv_usec=end.tv_usec-start.tv_usec;
if(end.tv_usec<0){
end.tv_usec=end.tv_usec+1000000;
end.tv_sec--;
}
printf("gettimeofday for task=%d , %d sec %d usec\n",rank,end.tv_sec,end.tv_usec);
}
Esercizio 4: Metodo dei trapezi con comunicazioni di gruppo (1h)
Modificate il programma del punto (b) in modo che il nodo con indice 0 agisca da master sia per
la distribuzione degli intervalli su cui calcolare gli integrali (scatter) sia per la raccolta e
l’aggregazione dei risultati (gather). Gli altri nodi invece dovranno farsi carico della
computazione effettiva.
Confrontate le tempistiche di comunicazione, lo speedup e l’efficienza ottenuta con le
comunicazioni di gruppo rispetto a quanto ottenuto dai programmi dell’Es 3.
Versione (c)
#include "mpi.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
typedef double precision;
/*#define FUNCTION sin*/
#define FUNCTION myfunc2
precision myfunc1( precision x){
return x;
}
precision myfunc2( precision x){
return 3;
}
precision trapezoid( precision a, precision b, precision h, unsigned int iter){
precision sum;
precision x;
unsigned int i;
sum=0;
x=a;
for(i=0;i<iter-1;i++){
sum=sum+ ( 0.5 * h * ( FUNCTION(x) + FUNCTION(x+h)));
x=x+h;
//printf("partial sum of %d trapezoid=%lf \n",i+1,sum);
}
sum=sum+ ( 0.5 * h * ( FUNCTION(x) + FUNCTION(b)));
return sum;
}
check_mpi(int rc){
if (rc != MPI_SUCCESS) {
printf ("Error starting MPI program. Terminating.\n");
MPI_Abort(MPI_COMM_WORLD, rc);
}
}
#define START 1
#define END 4
#define NBROFTRAP 1000000000ULL
int main(int argc,char *argv[]){
int idx,numtasks, rank, rc, resultlen;
char name[100];
double time_start, time_end;
precision a=START;
precision b=END;
precision h;
precision sum;
precision sum_rec;
precision sum_send;
double n=NBROFTRAP;
unsigned int iter,iter_last;
int dest, source, tag , count, sendcount, recvcount;
MPI_Status Stat;
MPI_Request reqs[2];
precision sendbuf[10][4];
precision recvbuf[4];
precision recvbuf_gat[10];
struct timeval start,end;
gettimeofday(&start,NULL);
memset(sendbuf,0x0,sizeof(sendbuf));
sum=0;
time_start=MPI_Wtime();
printf ("Time start for task=%d : %fl\n",rank,time_start);
rc = MPI_Init(&argc,&argv);
check_mpi(rc);
MPI_Comm_size(MPI_COMM_WORLD,&numtasks);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Get_processor_name(name,&resultlen);
printf ("Number of tasks=%d My rank=%d on %s\n", numtasks,rank,name);
if(rank==0){
h=(b-a)/n;
iter=n/(numtasks-1);
iter_last=n-(iter*(numtasks-2));
for(idx=1;idx<numtasks;idx++){
a=START;
a=a+((idx-1)*iter*h);
b=a+(iter*h);
sendbuf[idx][0]=a;
sendbuf[idx][1]=b;
sendbuf[idx][2]=h;
sendbuf[idx][3]=iter;
}
sendbuf[numtasks-1][1]=END;
sendbuf[numtasks-1][3]=iter_last;
}
recvcount=4;
sendcount=4;
source=0;
MPI_Scatter(sendbuf,sendcount,MPI_DOUBLE,recvbuf,recvcount,
MPI_DOUBLE,source,MPI_COMM_WORLD);
if(rank!=0){
a=recvbuf[0];
b=recvbuf[1];
h=recvbuf[2];
iter=recvbuf[3];
printf("rank= %d Results: a=%lf b=%lf h=%lf iter=%lf\n",rank,recvbuf[0],
recvbuf[1],recvbuf[2],recvbuf[3]);
sum=trapezoid(a,b,h,iter);
printf("task=%d sum=%lf\n",rank,sum);
}
recvcount=1;
sendcount=1;
source=0;
MPI_Gather(&sum,sendcount,MPI_DOUBLE,recvbuf_gat,recvcount,MPI_DOUBLE,source,MPI_COMM_WOR
LD);
if(rank==0){
sum=0;
for(idx=1;idx<numtasks;idx++){
printf("rank= %d Results: sum=%lf \n",idx,recvbuf_gat[idx]);
sum=sum+recvbuf_gat[idx];
}
printf("The value of the integral is: %lf\n", sum);
}
time_end=MPI_Wtime();
printf ("Time end for task=%d : %fl\n",rank,time_start);
printf ("Time for task=%d : %fl\n",rank,time_end-time_start);
MPI_Finalize();
gettimeofday(&end,NULL);
end.tv_sec=end.tv_sec-start.tv_sec;
end.tv_usec=end.tv_usec-start.tv_usec;
if(end.tv_usec<0){
end.tv_usec=end.tv_usec+1000000;
end.tv_sec--;
}
printf("gettimeofday for task=%d , %d sec %d usec\n",rank,end.tv_sec,end.tv_usec);
}
Tempistiche
funzione da integrare: f(x)=3
intervallo di integrazione: 1-4 (cm)
risultato di integrazione atteso: 9 (cm^2)
numero trapezi=10^9
Versione seriale:
Tempo su una esecuzione: 23,060 sec
Versione parallela:
Con p=2(1CPU)
2
Tp (sec)
To=pTp-Ts
S=Ts/Tp
E=S/p
a
13,188
3,316
1,748
0.874
b
12,771
2,482
1,805
0,902
c
22,879
22,698
1,007
0,503
Con p=7(1CPU)
7
Tp (sec)
To=pTp-Ts
S=Ts/Tp
E=S/p
a
3,867
4,009
5,96
0,85
b
3,655
2,53
6,31
0,9
c
4,310
7,11
5,35
0,76
Con p=14(1CPU)
14
Tp (sec)
To=pTp-Ts
S=Ts/Tp
E=S/p
a
1,997
4,898
11,55
0,82
b
1,920
3,82
12,01
0,86
c
2,319
9,41
9,94
0,71
Con p=20(1CPU)
20
Tp (sec)
To=pTp-Ts
S=Ts/Tp
E=S/p
a
1,391
4,760
16,58
0,83
b
1,404
5,02
16,42
0,82
c
1,470
6,34
15,69
0,78
numero trapezi=10^8
Versione seriale:
Tempo su una esecuzione: 2,474 sec
Versione parallela:
Con p=2(1CPU)
2
Tp (sec)
To=pTp-Ts
S=Ts/Tp
E=S/p
a
1,693
0,91
1,46
0,73
b
1,482
0,49
1,67
0,83
c
2,382
2,29
1,04
0,52
Con p=7(1CPU)
7
Tp (sec)
To=pTp-Ts
S=Ts/Tp
E=S/p
a
0,479
0,88
5,16
0,74
b
0,493
0,98
5,02
0,72
c
0,557
1,43
4,44
0,63
Con p=14(1CPU)
14
Tp (sec)
a
0,350
b
c
To=pTp-Ts
S=Ts/Tp
E=S/p
2,43
7,07
0,5
0,329
2,13
7,52
0,54
0,325
2,08
7,61
0,54
Con p=20(1CPU)
20
Tp (sec)
To=pTp-Ts
S=Ts/Tp
E=S/p
a
0,263
2,79
9,41
0,47
b
0,282
3,17
8,77
0,44
c
0,295
3,43
8,39
0,42
Risposte ai quesiti:
3(a) La versione dell'algoritmo che utilizza send e receive sincrone (a) per comunicare ottiene un
buono speedup rispetto alla versione seriale: aumentando il numero di processori coinvolti nella
computazione aumenta lo speedup (con una efficienza più o meno costante). Nel caso di 10^9
trapezi si passa da un tempo di esecuzione di 23,06 secondi per la versione seriale a 1,391 secondi
per la versione parallela con 20 processori; in questo caso abbiamo uno speedup pari a 16,58 e una
efficienza di 0,83.
3(b) La versione dell'algoritmo che utilizza send e receive asincrone (b) per comunicare ottiene
prestazioni del tutto simili a quelle dell'algoritmo con send e receive sincrone. In alcune casistiche i
due algoritmi hanno dei tempi di comunicazione differenti, con valori inferiori per la versione con
comunicazioni asincrone che risultano essere un pò meno bloccanti (“un pò meno” perchè
comunque ci sono delle barriere/wait) rispetto alla versione con comunicazioni sincrone.
4 Con la versione dell'algoritmo che utilizza comunicazioni di gruppo (c) notiamo subito un
fenomeno che ci aspettavamo: avendo più comunicazioni rispetto alle versioni con send e receive
abbiamo dei tempi di overhead maggiori e questo pregiudica il tempo di calcolo parallelo e quindi
anche speedup ed efficenza che sono inferiori rispetto a quelli degli algoritmi con send e receive.
Aumentando di un ordine di grandezza la dimensione del problema, passando da 10^8 triangoli a
10^9, notiamo che, a parità del numero di processori, speedup ed efficienza aumentano.
Aumentando il numero di processori, mantenendo costante la dimensione del problema, notiamo
che l'efficienza di minuisce.
Queste due caratteristiche ci portano a dire che la formulazione parallela del problema è scalabile:
riusciamo ad ottenere una efficienza costante aumentando la dimensione del problema e il numero
di processori coinvolti nella comunicazione.