Sunteți pe pagina 1din 9

6.3. Procese egale. Stabilirea topologiei

6.3.1 Difuzarea mesajelor folosind sondaje

În rezolvările multor probleme paralele, procesele realizează aceleaşi prelucrări, cooperând de pe poziţii egale pentru îndeplinirea unei funcţii.

Ca exemplu, vom considera problema difuzării mesajelor folosind sondaje. Fie i nodul sursă al informaţiei care se difuzează. O posibilă soluţie este utilizarea arborelui de acoperire T pentru a stabili căile de comunicare a informaţiei de la i la toate celelalte noduri. Astfel nodul i transmite informaţia împreună cu T vecinilor săi. Fiecare vecin inspectează T şi transmite informaţia împreună cu T fiilor săi din T, s.a.m.d.

Deoarece T este arbore de acoperire, informaţia va ajunge la fiecare nod, o singură dată ( comunicată de tatăl din T al nodului respectiv). Algoritmul este următorul:

type tip_arb = array [1:N,1:N] of int; chan sondaj[1:N](tip_arb,tip_mesaj); Nod(p:1 N)::

var arb:tip_arb, m : tip_mesaj; receive sondaj[p](arb,m); fa q:=1 to N st q este fiul lui p in arb -> send sondaj[q](arb,m)

af

Initiator::

var i: int := indexul nodului initiator; var top: array[1:N,1:N] of bool; var T : tip_arb, m : tip_mesaj; calculeaza T pe baza top; m := mesaj_de_transmis; send sondaj[i](T,m);

Mesajul este pregătit de un proces Iniţiator situat în nodul i şi este comunicat procesului Nod(i). Aceasta permite descrierea întregii reţele de difuzare ca o colecţie de procese egale. Soluţia presupune că procesul Iniţiator cunoaşte topologia reţelei, din care derivează arborele de acoperire T.

În cazul în care nodul iniţiator nu cunoaşte topologia, iar fiecare nod cunoaşte doar vecinii, difuzarea mesajului se poate face dacă fiecare nod retransmite mesajul primit

88

tuturor vecinilor săi, ignorând copiile acestuia. Pentru a furniza o soluţie complet simetrică, adoptăm convenţia că primul mesaj care se difuzează este retransmis chiar şi nodului de la care provine. În acest mod, fiecare nod cunoaşte precis numărul nodurilor de la care primeşte sau cărora le transmite mesajul. Soluţia are descrierea următoare:

chan sondaj[1:n](tip_mesaj); Nod(p:1 n)::

var leg: array [1:n] of bool := vecinii_lui_p; var num: int := numarul_vecinilor; var m: tip_mesaj; receive sondaj[p](m); fa q := 1 to n st leg[q] -> send sondaj[q](m); af; fa q := 1 to num-1 -> receive sondaj[p](m); af;

Initiator::

var i:int := indexul_nodului_initiator; var m: tip_mesaj; m := mesaj_de_transmis; send sondaj[i](m);

Comparând cele două soluţii, putem remarca următoarele:

- numărul de mesaje transmise de primul algoritm este n-1, în timp ce,în al doilea caz, este 2m, unde m este numărul de legături din reţea;

- mesajele transmise de al doilea algoritm sunt mai simple şi mai scurte, deoarece nu includ arborele de acoperire T.

În esenţă, arborele de acoperire este construit dinamic, el constând din legăturile pe care sunt primite primele mesaje de către noduri.

6.3.2. Stabilirea topologiei unei reţele de procese

Ca un alt exemplu, vom considera problema stabilirii topologiei unei reţele. Fiecare nod al reţelei este reprezentat de un proces, iar legăturile dintre noduri sunt realizate prin canale. Fiecare nod poate comunica doar cu vecinii săi, cu care are canale directe de legătura. Iniţial, fiecare nod cunoaşte aceste canale şi, implicit, vecinii. Problema este ca fiecare nod să determine topologia întregii reţele. Aceasta informaţie ar putea fi folosită în dirijarea mesajelor între noduri neadiacente. Vom reprezenta fiecare nod

Un tablou de legături, leg[1:N], local fiecarui proces,

printr-un proces Nod(p:1

va preciza vecinii nodului p:

pentru p, leg[q]=TRUE daca q este vecin cu p, altfel leg[q]=FALSE.

89

N).

Relaţia de vecinătate este simetrică, astfel că pentru p, leg[q]=TRUE <=> pentru q, leg[p]=TRUE.

Topologia care trebuie calculată este reprezentată în fiecare nod prin matricea de adiacenţă top, având elementele :

top[i,j]=TRUE , dacă i este vecin cu j top[i,j]=FALSE , în caz contrar. Evident, top se calculează folosind valorile cunoscute leg ale fiecărui proces p, astfel că, la terminarea calcului, următorul predicat este satisfacut:

TOPOLOGIE: ( pentru orice p,q: 1<=p,q<=N : top[p,q] = leg p [q] )

Problema are o rezolvare foarte simplă în cazul variabilelor partajate: este suficient

ca fiecare proces p sa actualizeze linia p a matricei top. Deoarece procesele folosesc elemente de matrice din linii diferite, non-interferenţa este asigurată.

O altă soluţie simplă este cea centralizată: fiecare proces trimite informaţii despre

vecinii săi unui proces central care alcătuieşte matricea de adiacenţă, pe care o retransmite apoi diferitelor noduri. Deoarece nu toate nodurile sunt vecine cu nodul central, rezolvarea este inevitabil asimetrică, unele noduri jucând şi rolul de intermediere a transferului de mesaje, în timp ce altele nu (prezentăm această soluţie ceva mai târziu).

Algoritmul pulsaţiilor pentru stabilirea topologiei

O soluţie uniformă este cea distribuită: fiecare proces calculează singur topologia,

folosind informaţiile provenite de la vecini. Dacă fiecare nod transmite vecinilor informaţia din matricea sa de adiacenţă şi, în acelaşi timp, primeşte matricea de adiacenţă a fiecărui vecin, după un rund complet, fiecare nod va dispune de informaţii despre noduri aflate la distanţa 2 de el. După două runde complete, orice nod va avea informaţii despre noduri aflate la distanţa 3, etc. În general, dupa r runde sunt completate liniile din top corespunzătoare nodurilor q aflate la o distanţă d <= r, adică următorul predicat este satisfăcut pentru fiecare proces p:

RUND: ( oricare ar fi q cu 1<=q<=N: dist(p,q) <=r => top[q,*] este completat ) unde dist(p,q) este distanţa de la p la q, adică lungimea căii celei mai scurte între cele două noduri.

90

Cunoscând diametrul D al unei reţele, putem face calculul topologiei după următorul algoritm:

type tip_top=array[1:N,1:N] of bool; tip_leg = array [1 :N] of bool; chan topologie[1:N] (tip_top);

Nod(p:1

var leg: tip_leg := vecinii_lui_p);

{initializat cu vecinii lui p} var top: tip_top := ([N*N]FALSE); top[p,1:N]:= leg; {actualizeaza linia vecinilor} var r: int := 0; var top_nou: tip_top; do r<D -> {transmite topologia curenta tuturor vecinilor} fa q :=1 to N st leg[q]-> send topologie[q](top)

N)::

af fa q :=1 to N st leg[q]-> receive topologie[p](top_nou); top := top or top_nou;

af r := r+1;

od

Algoritmul prezentat se numeşte algoritmul pulsaţiilor (inimii) - heartbeat - deoarece acţiunile fiecărui nod pot fi asemănate cu bătăile inimii: transmiterea informaţiilor către vecini este asemănătoare dilatării, în timp ce culegerea informaţiilor de la vecini este asemănătoare unei contracţii.

Algoritmul are două neajunsuri: unul este faptul că, în general, D nu este cunoscut; cel de al doilea este traficul inutil, anumite noduri primind mesaje chiar după actualizarea completă a tabloului top local.

Pentru a ocoli aceste neajunsuri, trebuie să observăm că, deoarece reţeaua este conexă, putem considera că un nod cunoaşte întreaga topologie dacă fiecare linie top are cel puţin un element TRUE. În acest moment, procesul p trebuie să mai execute un singur rund, după care se poate termina. Acest ultim rund este necesar pentru a transmite vecinilor informaţiile recente primite, eventual, la rundul precedent. Totodată, procesul poate culege ultimele mesaje care îi sunt transmise, evitând abandonarea mesajelor în canale. Deoarece un nod poate termina cu un rund înainte sau după vecinii săi, sunt necesare măsuri suplimentare de evitare a blocării

91

definitive: un proces transmite mesaje doar vecinilor care nu s-au terminat şi preia doar mesajele pe care aceştia le trimit.

Aceste elemente sunt surprinse în descrierea următoare:

type tip_top=array[1:N,1:N] of bool; tip_leg = array [1 :N] of bool; chan topologie[1:N](transm: int, gata : bool, top: tip_top);

Nod(p:1

var leg: tip_leg := (vecinii_lui_p);

var activ: tip_leg := leg;

var top: tip_top := ([N*N]FALSE); var gata: bool := FALSE; var transm: int, qgata: bool, top_nou: tip-top;

top[p,1:N] := leg; {actualizeaza vecinii lui p}

N)::

{vecinii activi}

do not gata -> {transmite topologia curenta tuturor vecinilor} fa q :=1 to N st leg[q]-> send topologie[q](p,FALSE,top)

af {receptioneaza topologiile de la vecini} fa q :=1 to N st leg[q]-> receive topologie[p](transm,qgata,top_nou); top := top or top_nou; if qgata -> activ[transm]:=FALSE fi

af if toate liniile din top au un element TRUE -> gata := TRUE

fi

od {transmite top vecinilor activi in ultimul rund} fa q :=1 to N st activ[q]-> send topologie[q](p,TRUE,top)

af fa q :=1 to N st activ[q]-> receive topologie[p](transm,qgata,top_nou);

af

Trebuie observat că primul ciclu se termină deoarece, reţeaua fiind conexă, informaţiile despre un nod se propagă la fiecare rund. Ca urmare, la un moment dat, toate liniile matricei top vor fi actualizate şi gata devine TRUE.

Pentru algoritmii distribuiţi, o măsură importantă de complexitate este numărul de mesaje comunicate între procese. În varianta centralizată, menţionată la începutul

92

secţiunii, se transmit 2*N mesaje: unul de la fiecare nod la serverul central şi câte unul de răspuns, de la server la fiecare nod. De data aceasta, numărul mesajelor este mai mare, dar mesajele se transmit doar între vecini. Dacă diametrul reţelei este D, iar gradul fiecărui nod nu depăşeşte valoarea m , numărul mesajelor este limitat superior de 2m*N*(D+1). Justificarea este că se execută D+1 runde, în care fiecare nod schimbă cu fiecare vecin două mesaje.

Stabilirea topologiei unei reţele are şi alte soluţii. Unele se bazează pe mesajele de sondaj cu ecou, la care ne referim în cele ce urmează. Ea foloseşte mesajele de sondaj pentru a difuza informaţia într-o reţea.

Stabilirea topologiei folosind mesaje de sondaj cu ecou

Rezolvarea bazată pe mesajele de sondaj cu ecou are următoarele etape: un iniţiator colectează informaţii despre topologia locală a tuturor celorlalte noduri, determină topologia întregii reţele şi o difuzează nodurilor. Colectarea se poate face în două faze: mai întîi, fiecare nod transmite cîte un mesaj de sondaj vecinilor şi recepţionează topologii ca ecouri; apoi, fiecare nod trimite topologia locală actualizată cu ecourile, nodului de la care a primit primul mesaj de sondaj. Iniţiatorul mesajului de sondaj primeşte informaţiile cumulate prin ecouri de la toate celelalte noduri. Ca urmare, el poate calcula topologia întregii reţele, pe care o transmite apoi prin legăturile arborelui de acoperire.

Pentru început, să presupunem că reţeaua este un graf aciclic, mai precis un arbore şi că nodul iniţiator i este rădăcina acestuia. Nodul i trimite un mesaj de sondaj tuturor vecinilor (fiilor). Ca urmare, mesajele se propagă spre frunze. Deoarece acestea nu au alţi vecini, ele încep faza de transmitere a ecourilor. Fiecare frunză trimite un mesaj cu topologia vecinilor săi, către părinte.

După adunarea mesajelor ecou de la fii, un nod le combină cu propria sa informaţie şi transmite rezultatul părintelui său. Rădăcina va primi informaţii de la toate celelalte noduri, obţinînd prin reuniune topologia reţelei.

const sursa=i; type tip_leg = array [1:n] of bool; tip_top = array [1:n, 1:n] of bool; chan sondaj[1:n](transm: int); chan ecou[1:n](top: tip_top); chan ecou_final(top: tip_top);

93

Nod(p:1 n)::

var leg: tip_leg := vecnii_lui_p; var top: tip_top := ([n*n] false);

top[p, 1

var top_nou: tip_top; var parinte: int; receive sondaj[p](parinte); {transmite sondaje celorlalte noduri vecine, copiii lui p} fa q := 1 to n st leg[q] and (q<>parinte) -> send sondaj[q](p);

af; {primeste ecourile si fa reuniunea lor} fa q := 1 to n st leg[q] and (q<> parinte) -> receive ecou[p](top_nou); top := top or top_nou;

af; if p=sursa -> send ecou_final(top); [] p<>sursa -> send ecou[parinte](top); fi;

n]

:= leg;

Initiator::

var top: tip_top; send sondaj[sursa](sursa); receive ecou_final (top);

În cazul general, adoptăm următoarele măsuri:

- ca şi mai înainte, după ce primeşte un mesaj de sondaj, un nod îl trimite tuturor

celorlalţi vecini, după care aşteaptă ecouri de la ei;

- datorită existenţei ciclurilor, două noduri îşi pot trimite reciproc sondaje; pentru

toate sondajele în afara primului, se transmite un ecou cu o topologie nulă; topologia locală va fi transmisă doar ca ecou la primul sondaj;

- pentru recepţia eventualelor sondaje primite pe durata aşteptării ecourilor, cele două tipuri de mesaje sunt recepţionate pe un acelaşi canal.

const sursa=i;

type fel = enum(sonda, ecou); type tip_leg = array [1:n] of bool; tip_top = array [1:n, 1:n] of bool; chan sonda-ecou[1:n](fel, transm: int, top:tip_top);

chan ecou_final(top: tip_top);

{index initiator}

Nod(p:1 n)::

var leg: tip_leg := vecinii_lui_p; var top: tip_top := ([n*n] false); top[p, 1:n] := leg;

94

var top_nou: tip_top;

var prim: int;

var k:fel, transm:int; var nr_ecouri: int := numar_vecini - 1; receive sonda-ecou[p](k, prim, top_nou);

{transmite sondaje celorlalte noduri vecine, copiii lui p} fa q := 1 to n st leg[q] and (q<>prim) ->

{nodul de la care s-a primit prima sonda}

send sonda-ecou[q](sonda, p, O);

{topologie nula}

af; {primeste ecourile si fa reuniunea lor, sau sonde si ignora} do nr_ecouri>0 -> receive sonda-ecou[p](k, transm, top_nou); if k=sonda -> send sonda-ecou[transm](ecou, p, O); [] k=ecou -> top := top or top_nou; nr_ecouri := nr_ecouri-1;

fi

od; if p=sursa -> send ecou_final(top); [] p<>sursa -> send sonda-ecou[prim](ecou, p, top); fi;

Initiator::

var top: tip_top; send sonda-ecou[sursa](sonda, sursa, O); receive ecou_final (top);

Procesul Nod(p) transmite un mesaj de sondaj vecinilor, aşteptând apoi ecourile. Ciclul de aşteptare se încheie cînd toţi vecinii au transmis ecourile (în algoritm se face doar contorizarea ecourilor). În acest moment, canalul sonda-ecou[p] nu mai conţine nici un mesaj, deoarece orice proces q transmite un eventual mesaj de sondaj înainte de ecou, sondaj ce este prelucrat înaintea ecoului (ciclul de transmitere a sondajelor precede ciclul de transmitere a ecourilor).

În privinţa numărului de mesaje, prezentul algoritm foloseşte un număr mai mic de mesaje decît algoritmul pulsaţiilor: fiecare legătură (aparţinând arborelui de acoperire) corespunzătoare primei sonde poartă două mesaje (sonda şi ecoul), iar celelalte cîte patru (două sonde şi două ecouri). Transmiterea topologiei de la iniţiator la noduri necesită alte n mesaje.

Ca şi la difuzarea mesajelor folosind listele vecinilor, şi aici arborele de acoperire este stabilit dinamic, constînd din legăturile de la fiecare nod la nodul prim, de la care acesta a recepţionat primul mesaj de sondaj.

95

Exerciţii

1) Problema mariajului stabil. Fie două tablouri a câte n procese, B (bărbaţi) şi F

(femei). Fiecare bărbat clasifică femeile de la 1 la n şi fiecare femeie clasifică bărbaţii de la 1 la n. Un mariaj este o corespondenţă unu-la-unu B-F. O împerechere este stabilă dacă pentru oricare doi bărbaţi b1, b2 şi pentru corespondentele lor f1 şi f2 sunt satisfăcute condiţiile următoare:

- b1 clasifică f1 mai sus decat f2 sau f2 clasifică b2 mai sus decat b1; şi

- b2 clasifică f2 mai sus decat f1 sau f1 clasifică b1 mai sus decat b2.

Altfel spus, un mariaj este instabil dacă există un bărbat şi o femeie care preferă fiecare perechea celuilalt. O soluţie a problemei este o mulţime de n perechi b-f, toate stabile.Scrieţi un program de rezolvare a problemei mariajului stabil, în care procesele comunică doar prin mesaje asincrone.

2) Imperechere distribuită. Sunt date n procese, fiecare corespunzând unui nod dintr- un graf. Fiecare nod poate comunica doar cu vecinii săi. Fiecare proces încearcă să se împerecheze cu un vecin. La terminarea împerecherii, fiecare proces trebuie să fie împerecheat sau singur, dar nu trebuie să existe două procese care sunt vecine şi sunt ambele neîmperecheate.

3) Arbore de acoperire. Sunt date n procese, fiecare corespunzând unui nod dintr-un graf. Fiecare nod poate comunica doar cu vecinii săi. Scrieţi un program pentru construirea arborelui de acoperire, fără a afla mai înainte topologia. Deci, fiecare proces va decide împreună cu vecinii săi care legături să fie puse în arbore şi care nu.

4) Se dau trei procese, fiecare avand o secvenţă de numere întregi ordonată crescător. Există cel puţin o valoare comună celor trei secvenţe. Scrieţi programul care găseşte cea mai mică valoare comună.

96