Documente Academic
Documente Profesional
Documente Cultură
• OCCAM
• APLICATII PARALELE PE UN HIPERCUB
• MPI
• PVM
CARACTERISTICI ALE PROGRAMARII
MULTICALCULATOARELOR
-nu exista spatiu unic de adresa (programarea fara memorie partajata);
-programul paralel se executa ca o multime de procese concurente, numai cu
memorie locala;
-comunicatia intre procese se face prin transfer de mesaje (operatie „send”
dintr-un proces <-> operatie „receive” a altui proces);
-nu exista variabile partajate (fara mecanisme de protectie a accesului la
acestea).
Biblioteci de comunicatie:
-PVM: Parallel Virtual Machine;
-MPI: Message Passing Interface.
LIMBAJUL OCCAM
-algoritm paralel -> colectie de procese asincrone, care comunica prin canale;
-asignare:
variabila := expresie
-SEQ creaza un proces complex dintr-un numar de procese minore care se vor
executa secvential:
SEQ
proces_1
proces_2
proces_3
........
proces_n
-PAR creaza un proces complex care lanseaza executia paralela a mai multor
procese:
PAR
proces_1
proces_2
proces_3
........
proces_n
-IF creaza un proces complex care lanseaza in executie unul din procesele
componente, corespunzand primei conditii adevarate (testarea se face in ordinea scrierii
conditiilor):
IF
conditie_1
proces_1
conditie_2
proces_2
conditie_3
proces_3
........
conditie_n
proces_n
-ALT creaza un proces complex care lanseaza in executie un singur proces
corespunzand primului canal de intrare care este gata de comunicatie:
ALT
canal_1 ? x
proces_1
canal_2 ? x
proces_2
canal_3 ? x
proces_3
...........
canal_n ? x
proces_n
1
4
0 1 x 2 dx 4(arctg (1) arctg (0))
=> prin integrare numerica (inaltimea fiecarui dreptunghi este valoarea functiei in
mijlocul subintervalului respectiv).
Algoritmul secvential:
#define n . . . . .
sum = 0.0;
w = 1/n;
for (i=0; i<n; i++) {
x = (i+0.5)*w;
sum += 4/(1+x*x);
}
sum *=w;
SEQ
partial_sum:=0.0
w:=1/n
x:=((i*s)+0.5)*w
SEQ j=0 FOR s-1
SEQ
partial_sum:=partial_sum+(4/(1+x*x))
x:=x+w
partial_sum=partial_sum*w
--transferul sumelor partiale si acumulare in procesorul 0
IF
i=p-1
chan[p-1] ! partial_sum
i<p-1
SEQ
REAL sum:
chan[i+1] ? sum
chan[i] ! sum+partial_sum
APLICATII PARALELE PE UN HIPERCUB
Algoritmi pe un hipercub
Hipercubul conecteaza fiecare din toate cele P taskuri (procesoare) la log2P alte taskuri.
-model SPMD;
-initial: o variabila de stare <- o data furnizata la intrare;
-log2P pasi;
-la fiecare pas fiecare task:
-schimba starea locala cu unul din vecini;
-combina mesajul primit de la vecin cu starea curenta -> starea urmatoare;
=> rezultatul: starea generata la pasul final.
procedure hypercube (myid, input, logp, output)
begin
state=input //starea locala init cu input
for i=0,logp-1 //repeta de logp ori
dest=myid XOR 2i //determina vecinul
send state -> dest //schimba datele
receive message <- dest
state=OP(state,message) //executa operatia
endfor
output=state //rezultatul final
end
Reducerea a patru vectori de lungime N=4 distribuiti la patru taskuri => algoritmul in
log24=2 pasi. La fiecare pas fiecare task:
-executa schimbul a N date cu un vecin;
-realizeaza N operatii de combinare.
In cazul general de reducere vectoriala fiecare task dintre P taskuri:
-furnizeaza un vector de N valori;
-executa N operatii separate de reducere;
=> vector de N rezultate. Reducerea: in log2P pasi.
Timpul necesar:
unde
-top = timpul cerut de operatia de reducere;
-tw = timpul de comunicatie;
-ts = timpul de lansare (start-up).
Algoritmul este eficient pentru valori mici ale lui N (cand predomina timpul de lansare).
Varianta a algoritmului: algoritm recursiv cu injumatatire => reducerea semnificativa a
volumului mesajelor comunicate. Algoritmul aplicat de doua ori: in faza de reducere
fiecare procesor comunica si combina N/2 date in prima iteratie, apoi jumatate N/4 in a
doua, etc. => fiecare procesor comunica in total N(P-1)P date in log2P pasi. Se obtine
suma globala, iar vectorul rezultat de N componente este uniform distribuit la cele P
procese.
Timpul consumat:
P 1
Treducere recursiva t s 2 log 2 P (t w 2 top ) N
P
Solutia trimite de doua ori mai multe mesaje, dar cu mai putine date si face mai putine
calcule => mai eficient pentru anumte valori ale lui N si P, si pe anumite masini.
Broadcast
Matricea A de transpus si transpusa A' sunt distribuite printre procese => executia
algoritmului implica comunicatii.
Timpul necesar:
N2
Ttranspunere simpla t s ( P 1) t w ( P 1) 2
P
N=P=8 => log2P pasi.. Fiecare proces are o singura coloana din A, iar in final fiecare
va avea o singura linie din A'.
La fiecare pas fiecare proces: schimba ½ din datele sale (reprezentate umbrit in desen).
In partea a doua a desenului: sursele componentelor detinute de procesul 0, la fiecare
pas al algoritmului.
Procesele sunt partitionate in doua seturi: taskurile corespunzatoare din cele doua seturi
interschimba ½ dintre datele lor: taskurile 0…p/2-1 comunica jumatatea inferioara a
datelor lor, iar taskurile P/2…P-1 comunica jumatatea superioara. Aceasta partitionare
si interschimb se repeta pana cand fiecare set contine un singur task.
Daca fiecare dintre cele log2P mesaje are dimensiunea N2/(2P) timpul necesar este:
N2
Ttranspunere hipercub t s log 2 P t w log 2 P
2P
Algoritmul hipercub transmite aprox. P/log2P mai putine mesaje, dar de (log2P)/2 ori
mai multe date.
Operatorul logic AND este utilizat pentru a determina daca taskul este "high" sau "low"
intr-un interschimb particular, iar myid si i sunt ca in algoritmul general.
Mergesort. Algoritmul “parallel mergesort" (fiecare task):
N d (d 1) d (d 1)
Tcomp tc N log 2 tc N log 2 N
P 2 2
Algoritmul perfect echilibrat
=> se presupune ca timpii inactivi sunt neglijabili. Astfel:
Tcomp N d (d 1) d (d 1) N d (d 1)
T Tcomunicatie tc log 2 N ts tw
P P 2 2 P 2
N N (log 2 P) 2
tc ts tw daca (log 2 P) 2 log 2 N
2P P 2
Programarea aplicatiilor paralele pe nCUBE-2
int nwrite (char * buffer, int dim, int address, int type);
unde:
type: tipul de transfer de mesaje (sincron sau asincron);
buffer: adresa de memorie (in procesul care executa functia) de unde se
transmite mesajul;
dim: lungimea mesajului;
address: adresa de destinatie a mesajului (un nod, grup de noduri la
"multicast", toate nodurile la "broadcast").
Receptia (blocanta):
int nread (char * buffer, int dim, int source, int type);
void whoami (int * node, int * res1, int *res2, int *cube);
unde:
node: pointer la variabilele in care whoami depune numarul (eticheta) nodului;
cube: pointer la variabila in care se depune dimensiunea subcubului alocat
programului curent;
res1, 2: nu se utilizeaza.
Proiectarea algoritmului:
-se partitioneaza programul intr-un numar de procese;
-se distribuie aceste procese nodurilor sistemului.
Exemplu: calculul aproximatiei numarului π.
Fiecare nod:
-executa functia main (fiecare nod are o copie) -> calcule locale;
-apeleaza functia de transfer de mesaje fan_in.
n
T p O( log 2 p)
p
-accelerarea:
p
S O
1 p log 2 p
n
-eficienta:
1
E O
1 p log 2 p
n
BIBLIOTECA MPI
Avantaje:
-portabilitatea codului sursa;
-implementari eficiente pe o varietate de platforme (inclusiv Unix si
Windows NT/XP);
-functionalitate (tipuri diferite de comunicatie, tipuri de date definite
de utilizator, topologii definite de utilizator);
Dezavantaje:
-mecanismul de lansare a unei aplicatii MPI dependent de platforma;
-nu permite gestiunea dinamica a proceselor (modificarea numarului
de procese in timpul rularii).
Forma generala a unui program MPI:
MPI_Init (&argc,&argv)
MPI_Comm_size (comm,&size)
MPI_Comm_rank (comm,&rank)
MPI_Finalize ()
#include "mpi.h"
#include <stdio.h>
int main(int argc,char *argv)
{
int numtasks, rank, rc;
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);
printf ("Number of tasks= %d My rank= %d\n", numtasks,rank);
/******* do some work *******/
MPI_Finalize();
return 0;
}
Comunicatii punct-la-punct
MPI_Send (&buf,count,datatype,dest,tag,comm)
care, transmite din zona buf, count date de tipul datatype la procesul destinatie dest,
din cadrul comunicatorului comm, mesajul avand identificatorul tag.
Pentru receptie:
MPI_Recv (&buf,count,datatype,source,tag,comm,&status)
datele fiind recptionate in zona buf la procesul care a apelat functia, in lungime
count de tipul datatype, de la procesul sursa source din cadrul comunicatorului comm,
mesajul avand identificatorul tag. Informatii de stare pentru mesajul receptionat sunt
depuse la status.
Exemplu: utilizarea functiilor de transmisie / receptie blocante.
#include "mpi.h"
#include <stdio.h>
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
dest = 1;
source = 1;
rc = MPI_Send(&outmsg, 1, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
rc = MPI_Recv(&inmsg, 1, MPI_CHAR, source, tag, MPI_COMM_WORLD, &Stat);
}
else if (rank == 1) {
dest = 0;
source = 0;
rc = MPI_Recv(&inmsg, 1, MPI_CHAR, source, tag, MPI_COMM_WORLD, &Stat);
rc = MPI_Send(&outmsg, 1, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
}
MPI_Finalize();
return 0;
}
Transmisia si receptia neblocante:
Initierea transmisiei:
MPI_Isend (&buf,count,datatype,dest,tag,comm,&request)
Initierea receptiei:
MPI_Irecv (&buf,count,datatype,source,tag,comm,&request)
MPI_Wait (&request,&status)
functie blocanta si
MPI_Test (&request,&flag,&status)
functie neblocanta pentru o singura cerere transmisie / receptie
MPI_Waitall (count,&array_of_requests,&array_of_statuses)
functie blocanta si
MPI_Testall (count,&array_of_requests,&flag,&array_of_statuses)
#include "mpi.h"
#include <stdio.h>
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
prev = rank-1;
next = rank+1;
if (rank == 0) prev = numtasks - 1;
if (rank == (numtasks - 1)) next = 0;
MPI_Finalize();
return 0;
}
Comunicatii colective
-un numar mai mare de procese comunica intre ele in diferite moduri;
MPI_Bcast (&buffer,count,datatype,root,comm)
Procesul root trimite blocul de date din buffer de lungime count si tip datatype la toate
procesele din cadrul comunicatorului, fiecare proces plasand datele in memoria sa in
zona buffer.
Scatter: un bloc de date (un tablou de un anumit tip) de la un proces este
impartit in bucati si distribuit uniform la diferite procese.
MPI_Scatter (&sendbuf,sendcnt,sendtype,&recvbuf,
recvcnt,recvtype,root,comm)
Procesul root trimite din zona sendbuf de lungime sendcnt si tip sendtype la fiecare
proces al comunicatorului comm cate un set de date depus in zona recvbuf, de lungime
recvcnt si tip recvtype.
Gather colecteaza blocuri de date de la un grup de procese si le reasambleaza
in ordinea corecta la un singur proces.
MPI_Gather (&sendbuf,sendcnt,sendtype,&recvbuf,
...... recvcount,recvtype,root,comm)
Procesul root receptioneaza in recvbuf de lungime recvcount si tip recvtype cate un bloc
de date de la fiecare proces al comunicatorului comm din zona sendbuf lungime sendcnt
si tip sendtype.
Reducere: un singur proces (procesul radacina) colecteaza datele de la celelalte
procese dintr-un grup si le combina pe baza unei operatii intr-o singura data.
MPI_Reduce (&sendbuf,&recvbuf,count,datatype,op,root,comm)
Datele de la toate procesele comunicatorului comm din zona sendbuf lungime count si
tip datatype sunt prelucrate cu operatia de reducere op si rezultatul inscris la procesul
root in zona recvbuf.
Operatia poate sa fie:
Sincronizare: prelucrarile pot continua numai daca toate procesele au ajuns
intr-un anumit punct al prelucrarilor.
MPI_Barrier (comm)
#include "mpi.h"
#include <stdio.h>
#define SIZE 4
if (numtasks == SIZE) {
source = 1;
sendcount = SIZE;
recvcount = SIZE;
MPI_Scatter(sendbuf,sendcount,MPI_FLOAT,recvbuf,recvcount,
MPI_FLOAT,source,MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}
Biblioteca PVM
-functii portabile de nivel inalt pentru C si Fortran -> comunicatia in cadrul
unui grup de procese;
-compozitia grupului de procese este dinamica => cost suplimentar destul de
ridicat;
-un multicalculator (o retea de calculatoare) -> calculator virtual paralel, prin
transfer de mesaje;
-permite controlul proceselor, transmiterea si receptia mesajelor, sincronizarea
intre procese.
pvm_start_pvmd
pvm_setopt
Pe procesorul server master : hostfile (numele statiilor, caile de fisiere executabile, caile
pentru procesele server (demonii) din fiecare statie, contul utilizatorului, parola etc.).
pvm_addhosts
Excludere statii:
pvm_delhosts
Oprirea masinii virtuale:
pvm_halt
Exemplu:
numt=pvm_spawn ("my_task", NULL, PvmTaskDefault, 0, n_task, tids);
creaza n_task procese care executa programul "my_task". Rezultatul functiei este
numarul efectiv de procese create (numt). Identificatorul fiecarui task creat este depus
intr-un element al vectorului tids.
main()
{
int cc, tid, msgtag;
char buf[100];
if (cc == 1) {
msgtag = 1;
pvm_recv(tid, msgtag);
pvm_upkstr(buf);
printf("from t%x: %s\n", tid, buf);
} else
printf("can't start hello_other\n");
pvm_exit();
}
Modele de programare ale interfetei PVM
-SPMD (Single Program Multiple Data): n instante ale aceluiasi program sunt
lansate ca n taskuri ale unei aplicatii paralele, folosind comanda spawn de la consola
PVM sau manual in cele n statii simultan. Initializarea mediului de programare:
specificarea statiilor pe care se executa cele n taskuri (nu se apeleaza pvm_spawn).
-MPMD (Multiple Program Multiple Data): unul sau mai multe taskuri sunt
lansate in diferite statii si acestea creaza dinamic alte taskuri.
Comunicatia punct-la-punct
pvm_pack
Transmiterea mesajului:
pvm_unpack
Receptia neblocanta:
pvm_nrecv
Comunicatia colectiva:
=> 0 pentru procesul care creaza grupul si valoarea cea mai mica disponibila in grup
pentru fiecare proces urmator (un proces poate sa apartina la unul sau mai multe
grupuri).
int pvm_gather (void *g_arrray, void *myarray, int dim, int type, int msgtag,
char *group_name, int root);
distribuie uniform tuturor proceselor din grup un vector de date de tipul type, cu numele
s_array si de dimensiune dim, aflat in spatiul de adresa al procesului radacina root.
Fiecare proces apeleaza pvm_scatter. Receptia se face in vectorul my_array.
int pvm_reduce (int operation, void *myvals, int dim, int type, int msgtag, char
*group_name, int root);
efectueaza operatia de reducere paralela intre toate procesele membre ale unui grup.
Argumentul operation defineste operatorul de reducere (PvmMin, PvmMax, PvmSum,
PvmProduct). Fiecare proces efectueaza operatia de reducere a datelor din vectorul
local de date myvals, de tipul type, de dimensiune dim, iar valoarea rezultata este
transferata procesului radacina root, care obtine valoarea de reducere finala.
sincronizeaza procesele membre ale unui grup (procesul este blocat pana cand un numar
ntasks procese din grupul group_name au apelat aceasta functie).
Exemplu: operatia de reducere paralela in masina virtuala PVM.
#include ........
#include "pvm3.h"
#define NTASKS 4
int main () {
int mytid, tids[NTASKS-1], groupid, sum, info;
/*crearea grupului de comunicatie*/
mytid=pvm_mytid();
groupid=pvm_joingroup("summax");
sum=groupid;
/*primul proces creaza celelalte NTASK-1 procese*/
if (groupid==0) {
info=pvm_spawn("summax",NULL,PvmTaskDefault," ", NTASKS-1,
tids);
printf("GroupId=%d spawned %d tasks\n", groupid, info);
}
/*bariera prin inghetare pana ce NTASKS procese s-au alaturat grupului*/
pvm_freezgroup("summax", NTASKS);
/*calculul sumei in grup*/
pvm_reduce (PvmSum, &sum, 1, PVM_INT, 1, "summax", 0);
/*procesul 0 tipareste rezultatul*/
if (groupid==0) {
printf("sum=%d\n", sum);
}
/*sincronizare pentru ca toate procesele sa execute operatia
inainte de parasirea grupului*/
pvm_barrier ("summex", NTASKS);
pvm_lvgroup ("summax");
pvm_exit();
return 0;
}