Documente Academic
Documente Profesional
Documente Cultură
03.simulare de Sisteme
03.simulare de Sisteme
Specificarea problemelor.
O problemă se poate specifica la nivelul interfeţei dintre algoritmul ce o rezolvă şi mediul extern.
O specificare de problemă P este o mulţime de evenimente de intrare in(P), o mulţime de evenimente de ieşire out(P) (cele două mulţimi constituind interfaţa lui P) şi o
mulţime seq(P) de secvenţe admisibile de evenimente de intrare şi evenimente de ieşire (care precizează interleavingurile permise dintre intrări şi ieşiri).
Fiecare eveniment de intrare sau de ieşire are un nume şi poate avea asociate date ca parametri.
(Condiţia 1 arată că utilizatorul şi algoritmul interacţionază propriu; ea impune utilizatorului un mod corect de comportare. Condiţia 2 afirmă că nu doi utilizatori simultan în
secţiunea critică).
Sisteme de comunicaţie
În problemele de simulare vom fi interesaţi în implementarea unor anumite tipuri de sisteme de comunicaţie între procesoare pe baza unor primitive.
Aceste sisteme de comunicaţie vor fi descrise cu ajutorul unei specificaţii de problemă.
Exemple.
interfaţa:
sendi (M): eveniment de intrare asociat procesorului pi care trimite o mulţime M (eventual vidă) de mesaje. Fiecare mesaj include o indicaţie a trimiţătorului şi a
destinatarului; există cel mult un mesaj pentru fiecare destinatar şi perechea transmiţător-destinatar trebuie să fie compatibilă cu topologia sistemului.
recvi(M): eveniment de ieşire asociat procesorului pi în care o mulţime M de mesaje este primită. Fiecare mesaj trebuie să-l aibă pe pi ca destinatar.
mulţimea secvenţelor admisibile.
Fie o secvenţă de evenimente de intrare şi ieşire.
Notăm cu R mulţimea tuturor mesajelor ce apar în toate evenimentele recv i(M) din şi cu S mulţimea tuturor mesajelor ce apar în toate evenimentele send i(M) din .
Există o funcţie K:RS care asociază fiecărui mesaj din R un mesaj cu acelaşi conţinut din S a.î.:
Integrity: K este bine definită. Orice mesaj primit a fost în prealabil trimis. Nu este permisă corupţia mesajelor.
No duplicates: K este injectivă. Nici un mesaj nu este primit mai mult de o dată.
Broadcast asincron
Integrity: K este bine definită (orice mesaj primit a fost în prealabil trimis).
No duplicates: Pentru ficare procesor pi 0 i n-1, restricţia lui K la evenimentele bc-recvi este
injectivă (nici un mesaj nu este primit mai mult de o dată de un procesor).
Liveness: Pentru ficare procesor pi 0 i n-1, restricţia lui K la evenimentele bc-recvi este surjectivă
(orice mesaj trimis este primit de fiecare procesor).
Observaţie: Din nou s-a presupus că nu există defecţiuni.
Procese
Pe fiecare procesor se vor executa bucăţi de cod care implementează sistemul de comunicaţie dorit. Deci nu va mai fi corect să identificăm "algoritmul" cu procesorul,
pentru că avem procese multiple ce se execută pe acelaşi procesor. Vom folosi noţiunea de nod iar sistemul de comunicaţie va fi reprezentat ca o cutie neagră în care este implementată
o "stivă de protocoale".
mediul extern
Nivelul 1
Nivelul 2
Nivelul 3
sistemul de comunicaţie
Un eveniment este activat într-o stare a procesului dacă există o tranziţie din acea stare, etichetată cu acel eveniment.
Un eveniment de intrare poate fi activat în orice stare a procesului.
Evenimentele de ieşire sunt activate de funcţia de tranziţie a procesului.
Intrările din mediul extern şi din sistemul de comunicaţii sunt intrările nodului.
Procesele de pe un singur nod interacţionează a.î. numai un număr finit de evenimente (diferit de intrările nodului) sunt activate de o intrare în nod.
O configuraţie a sistemului specifică o stare pentru orice proces de pe orice nod (configuraţia nu include starea sistemului de comunicaţie). O configuraţie iniţială conţine numai stări
iniţiale ale proceselor.
O execuţie a sistemului este un şir C0, 1, C1, 2,C2,.... alternativ de configuraţii şi evenimente, începând cu o configuraţie şi, dacă este finit, sfârşind cu o configuraţie a.î.:
Observaţie: Din 3. şi 4. rezultă că un nod este declanşat de o intrare în nod (din exterior sau din sistemul de comunicaţie) şi această declanşare cauzează un lanţ de evenimente pe acest
nod care apare atomic până ce alet evenimente nu mai sunt declanşate.
Planificarea unei execuţii este şirul evenimentelor din execuţie.
Dacă este o execuţie atunci top() (respectivc bot()) este restricţia planificării lui la evenimentele din inerfaţa top (respectiv bottom) a nivelului top (respectiv bottom).
O execuţie este fair dacă orice eveniment (diferit de o intrare într-un nod) care este activat va apare (odată şi odată; execuţia nu se opreşte prematur, când mai există paşi de urmat).
O execuţie are calitatea de a fi user-compliant pentru o specificaţie de problemă P, dacă mediul extern satisface restricţiile de intrare ale lui P: pentru orice prefix ' a lui unde
este o intrare din mediul extern, dacă ' este prefix al unui element din seq(P), atunci aşa este şi '.
O execuţie este corectă pentru sistemul de comunicaţie C dacă bot() este un element al lui seq(C). (Sistemul de comunicaţie este corect, în conformitate cu specificaţiile de problemă
a lui C).
O execuţie este (P,C) – admisibilă dacă estge fair, user – compliant pentru specificaţia de problemă P şi corectă pentru sistemul de comunicaţii C.
Atunci când P şi C sunt clare din context execuţia se va numi admisibilă.
Simulare
Sistemul de comunicaţii C, simulează (global) sistemul de comunicaţii C2 dacă există o colecţie de procese unul pentru fiecare nod numit Sim (procesul de simulare) care
satisface:
1. Interfaţa top a lui Sim este interfaţa lui C2.
2. Interfaţa bottom alui Sim este interfaţa lui C1.
3. Pentru orice execuţie (C2, C1) – admisibilă a lui Sim există un şir în seq(C2) a.î. =top().
(Executia simulării în topul sistemului de comunicaţie C1 produce aceleaşi apariţii în mediul extern pe care le face sistemul de comunicaţii C2).
C2
Sim
C1 input C1 output
C1
3'. Pentru orice execuţie (C2, C1) – local admisibilă a lui Sim seq(C2) a.î. |i=top()|i i 0 i n-1.
Convenţii de pseudocod
Pentru a specifica calitatea unui serviciu de broadcast (proprietăţi ce vor fi descrise mai jos) interfaţa descrisă mai anterior pentru un serviciu broadcast de bază se modifică
astfel:
bc-sendi(m,qos): eveniment de intrare asociat procesorului p i, care trimite un mesaj m tuturor procesoarelor; qos este un parametru ce descrie calitatea serviciului
cerut.
bc-recvi(m,j,qos): eveniment de ieşire în care procesorul pi primeşte mesajul m în prealabil difuzat de pj; qos este ca mai sus.
Observaţie: Atunci când ne referim la un serviciu broadcast de bază vom considera qos=basic. Un mesaj difuzat este primit şi de transmiţător.
Operaţiile bc – send şi bc – recv nu sunt blocante: nu se aşteaptă ca un mesaj să fie primit de toate procesoarele iar operaţia bc – recv acţionează în modul de lucru bazat pe întreruperi.
Se pot discuta două categorii de calităţi prin adăugarea de noi proprietăţi pentru secvenţele admisibile (pe lângă cele ale situaţiei qos=basic: integrity, no duplicates, liveness).
Ordonare
Totally Ordered: m1, m2 şi pi, pj dacă m1 este primit în pi înaintea lui m2 , atunci m2 nu este primit în pj înaintea lui m1.
În cele două condiţii, primit este un bc – recv cu qos pe una din cele două valori iar trimis este un bc – send cu qos corespunzător.
Pentru a defini al treilea model de broadcast cu ordonare este necesar să extindem relaţia de apariţie a unui eveniment înaintea altuia, la mesaje.
Se presupune că toate comunicările de la un nivel superior al aplicaţiei se execută numai cu bc – send şi bc – recv (nu există mesaje "behind of the scenes").
Definiţie: Dată o secvenţă de evenimente bc – send şi bc – recv spunem că mesajul m 1 se întâmplă înaintea mesajului m2 dacă
1. evenimentul bc – recv pentru m1 se întâmplă înaintea evenimentului bc – send pentru m2 sau
2. m1 şi m2 sunt trimise de acelaşi procesor şi m1 este trimis înaintea lui m2.
Causally Ordered: m1, m2 şi pi dacă m1 se întâmplă înaintea lui m2 atunci m2 nu-i primit de pi înaintea lui m1.
Exemple:
a a ,b
cauzal ordonat
dar nu-i t.o.
b b, a
a b b, a
t.o
dar nu-i cauzal ordonat
b, a
a b, a
a, b
b
Un sistem de broadcast este reliable (în prezenţa a f defecte) dacă pentru orice secvenţă de bc – send şi bc – recv sunt satisfăcute condiţiile: Integrity, No duplicates iar Liveness se
modifică astfel:
Nonfaulty Liveness: Dacă se consideră restrictia lui K la evenimentele asociate procesoarelor nedefecte atunci K este surjectivă (toate mesajele difuzate de un procesor nedefect sunt
primite odată şi odată de procesoarele nedefecte).
Faulty Liveness: Dacă un procesor nedefect are un eveniment bc – recv căruia K îi asociază un eveniment bc – send al unui procesor defect, atunci orice procesor nedefect are un
eveniment bc – recv căruia K îi asociază acelaşi eveniment bc – send (orice mesaj trimis de un procesor defect este sau primit de toate procesoarele nedefecte sau de niciunul).
Observaţie: Condiţiile nu depind de tipul de defecţiune a procesoarelor; vom considera totuşi numai defecţiuni de tip crash.
Anumite combinaţii de proprietăţilor ale serviciilor de tip broadcast au nume specifice date de importanţa lor practică:
Se implementează în topul unui sistem basic. Fiecare procesor asignează un număr de ordine pentru fiecare mesaj pe care îl difuzează; acesta este incrementat cu 1 ori de
câte ori un nou mesaj este difuzat. Primitorul unui mesaj de la p i cu număr de secvenţă T aşteaptă până se execută bc - recv i cu qos=ssf a tuturor mesajelor de la pi cu numărul de ordine
mai mic decât T.
3. t.o.
Un algoritm nesimetric
Se bazează din nou pe un sistem de broadcast basic. Pentru a difuza un mesaj m, procesorul p i îl trimite utilizând basic unui procesor central p c care asignează un număr de
ordine şi îl difuzează folosind basic.
Procesoarele execută receive pentru sitemul cu t.o. în ordinea specificată de numărul de ordine al mesajelor, aşteptând dacă este necesar până primesc toate mesajele cu
număr de ordine mai mic.
Clar, ordinea în care se execută receive pentru sistemul cu t.o. este aceeaşi la toate procesoarele şi anume cea în care au fost trimise de procesorul central.
Rolul procesorului central poate fi preluat şi de alte procesoare cu ajutorul unui token de identificare care să circule printre procesoare.
Un algoritm nesimetric
Explicaţii:
Fiecare procesor menţine un contor crescător: timestamp-ul său, folosit pentru a ştampila mesajele pe care vrea să le difuzeze. În plus, fiecare procesor are un vector în care
estimează timestamp-urile celorlalte procesoare. Intrarea j din acest vector semnifică faptul că p i nu va primi (încă o dată) un mesaj de la p j cu un timestamp mai mic sau egal cu acea
valoare.
Aceste componente ale vectorului sunt actualizate cu ajutorul timestampe-urilor asociate mesajelor sau folosind un mesaj special de actualizare trimis de p j.
Timestamp-ul propriu al fiecărui procesor va fi mai mare sau egal cu estimarea sa a timestamp-urilor celorlalte procesoare. Atunci când procesorul trbuie să-şi marească
timestamp-ul pentru a asigura condiţia precedentă va trimite un mesaj de actualizare a timestamp-urilor tuturor celorlalte procesoare.
Fiecare procesor menţine o mulţime de mesaje ce aşteaptă să fie primite (pentru sistemul cu t.o.). Un mesaj cu timestamp-ul T este primit t.o., atunci când orice intrare în
vector este mai mare sau egală decât T. Fie o execuţie fair a algoritmului care este corectă pentru sistemul de comunicaţii ssf.
Lema 1: Pentru procesor pi şi bc – sendi(m,to) din , pi asignează lui m un timestamp unic şi aceste timestamp-uri sunt asignate în ordine crescătoare în raport cu apariţiile lor în .
Lema 2: Timestamp-urile asignate mesajelor din , împreună cu id-urile procesoarelor formează o ordine totală (această ordine este numită ordinea timestamp).
Demonstraţie:
Trebuie demonstrat că au loc condiţiile:
Integrity, No Duplicates, Liveness şi Total Ordering.
Primele două sunt satisfăcute deoarece sistemul ssf pe care se bazează algoritmul le satisface.
Liveness: Presupunem că există un procesor pi care are o intrare în pending care aşteptă la nesfârşit. Fie <m,T,j> intrarea blocată în pending cu cel mai mic (T,j).
Întrucât procesoarele asignează timestamp-urile în ordine crescătoare odată şi odată <m,T,j> va fi cea mai mică intrare în mulţimea pending a lui p i. Pentru a fi blocată
trbuie să existe k a.î. T>ts[k] în p i la nesfârşit. Deci la un moment dat p i nu mai creşte ts[k]. Fie T' cea mai mare valoare pe care p i o dă lui ts[k]. Clar, ki. Deci de la un loc încolo p i nu
mai primeşte mesaje de la p k (în sistemul ssf). Deci p k nu va mai trimite mesaje cu timestamp-ul mai mare decât T'. Dar aceasta înseamnă că p k nu va primi mesajul <m,T,j> de la p j
contrazicând corectitudinea sistemului ssf.
Cazul I: <m2,T2,j2> este în pendingi atunci când bc – recv i(m1,j1,to) apare. Atunci, clar, (T1,j1)< (T2,j2) întrucât, altfel, m2 ar fi fost acceptat înaintea lui m1.
Cazul II: <m2,T2,j2> nu este în pendingi atunci când bc – recv i(m1,j1,to) apare.
Avem T1 tsi[j2]. Deci pi a primit un mesaj m de la p j2 (pe sistemul ssf) înaintea acestui moment cu timestamp-ul T T1. Din proprietatea ssf, p j2 trimite m2 după ce el trimite m
şi deci timestamp-ul T2 al lui m2 este mai mare decât T. Deci T2 > T1.
Cauzalitate
Să observăm că timestamp-ul asignat unui mesaj m difuzat de pi este strict mai mare decât timestamp-ul oricărui mesaj primit de pi după primirea mesajului m. Deci:
Observaţie: Cauzalitatea se poate obţine şi fără t.o., pornind de la un sistem de broadcast basic şi folosind timestamp-urile vectoriale. O raţiune pentru aceasta, este aceea că se poate
obţine calitatea c.o. chiar în prezenţă defecţiunilor în timp ce t.o. nu poate fi asigurată în prezenţa defecţiunilor.
Reliabilitate
Vom implementa un serviciu de broasdcast reliabil în topul unui sistem cu MP point-to-point în prezenţa a f căderi. Interfaţa sistemului MP point-to-point este fomată din
evenimente de intrare sendi(M), recvj(M) unde M este o mulţime de mesaje, ficare mesaj incluzând o indicaţie a trimiţătorului şi a primitorului. Secvenţele admisibile se definesc
având următoarea proprietate (procesoarele sunt partiţionate în defecte şi nedefecte primele fiind în număr de cel mult f).
Observaţii:
1: Calitatea de ssf se obţine exact ca în cazul lipsei căderilor.
2: Broadcast-ul t.o. reliabil nu se poate obţine atunci când sistemul pe care se bazează este asincron (altfel s-ar putea rezolva problema consensului în sisteme asincrone)
3: Broadcast-ul c.o. se obţine utilizând algoritmul dat pe un sistem de broadcast reliabil.
4: Toate problemele de implementare precedente pot fi extinse pentru a considera aşa numitul multicast în care un mesaj trebuie trimis la un grup de procesoare şi nu la toate.
Implementarile sunt simple atunci când grupurile sunt statice. Atunci când grupurile se pot modifica, în fiecare execuţie apar probleme la întreţinerea dinamică a lor.
Programul de simulare va fi numit MCS (memory consistency system) care este alcătuit din procese locale în fiecare procesor p i. Schematic:
Pentru simplitate, vom considera că obiectele din memoria partajată sunt regiştri read/write.De asemenea, nu vom considera defecţiuni ale procesoarelor.
Fiecare obiect partajat are o specificare secvenţială care indică comportarea dorită a obiectului în absenţa concurenţei.
O specificare secvenţială constă dintr-o mulţime de operaţii şi o mulţime de secvenţe de operaţii (mulţimea secvenţelor legale de operaţii).
REŢEAUA
DSM
Aceleaşi intrări şi ieşiri iar secvenţele admisibile satisfac Correct interaction, Liveness şi
Sequentially consistent: Există o permutare a operaţiilor din a.î.
1. pentru orice obiect O, |O este legală.
2. dacă răspunsul pentru operaţia o1 în nodul pi (oarecare) apare în înaintea invocării operaţiei o2 în nodul pi, atunci o1 apare înaintea lui o2 în .
Observaţii:
1. Se cere deci ca să se păstreze ordinea operaţiilor din acelaşi nod care nu se suprapun şi nu pentru toate operaţiile.
2. În exemplul precedent 2 este secvenţă consistentă, permutarea dorită este w0r0w1r1. Deci condiţia de liniarizare este mai tare decât condiţia de consistenţă secvenţială.
Există secvenţe care nu sunt secvenţial consistente:
3 = write0(x, 1) write1(y, 1) ack0(x) ack1(y) read0(y) read1(x) return0(y,0) return1(x,0).
Pentru a respecta semantica lui x şi y trebuie ca r 0 să preceadă w1 şi r1 să preceadă w0. Pentru a respecta ordinea evenimentelor din p 1 trebuie ca w1 să preceadă r1. Din tranzitivitate
rezultă că r0 precede w0 care încalcă ordinea evenimentelor în p0.
Algoritmi
Vom presupune că sistemul MP de bază suportă broadcast t.o. (am arătat cum poate fi implementat). Pentru a evita confuzia vom folosi:
Toţi algoritmii folosesc replicarea completă: există o copie locală a oricărui obiect partajat în starea procesului MCS din fiecare nod. (Aşa cum se face, de exemplu în ORCA;
gestionarea copiilor în alt stil este posibilă dar aceasta este o altă problemă).
Algoritm pentru simularea unui DSM liniarizabil
Atunci când procesul MCS dintr-un nod primeşte o cerere să citească sau să scrie un obiect partajat:
MCS trimite un mesaj broadcast conţinând cererea şi aşteaptă să-şi primească mesajul său.
După primirea mesajului, execută răspunsul pentru operaţia ce aşteaptă (returnând valoarea copiei obiectului pe care o deţine el pentru un read şi executând ack
pentru write).
Ori de câte ori un proces MCS primeşte un mesaj difuzat pentru un write, actualizează copia obiectului, în mod corespunzător.
Teoremă: Dacă sistemul de broadcast este total ordonat atunci algoritmul precedent simulează un DSM liniarizabil.
Demonstraţie: Fie o execuţie admisibilă a algoritmului (fair, user compliant pentru sistemul cu memorie partajata si corecta pentru sistemul broadcast de comunicaţie). Trebuie să
demonstrăm că top() este liniarizabil. Fie = top(). Ordonăm operatiile din după ordinea totală a broadcast-urilor asociate lor din şi obţinem o permutare .
respectă semantica obiectelor. Fie x un obiect read/write. |x sete secvenţa operatiilor ce accesează x. Cum broadcast-urile sunt t.o., orice proces MCS primeşte mesajele pentru
operaţiile asupra lui x în aceeaşi ordine, care este ordinea lui , şi gestionează x corect.
respectă ordonarea după timpul real de apariţie a operaţiilor în .
Presupunem că o1 se termină în înainte ca operaţia o2 să înceapă.
Atunci broadcast-ul corespunzător lui o 1, a fost primit de iniţiatorul său înainte ca broadcast-ul lui o 2 să fie trimis de iniţiatorul său. Evident, boadcastul lui o 2 este ordonat după cel al
lui o1. Deci operaţia o1 apare în înaintea lui o2.
Observaţie: Cum un serviciu broadcast t.o. se poate implementa în topul unui sistem MP point-to-point, rezultă că un DSM liniarizabil se poate implementa în topul unui sistem MP.
Nu este evident de ce trebuie difuzat un mesaj pentru operaţia read, atâta timp cât nu se modifică nici o copie a obiectului ca rezultat al primirii mesajului de citire.
Considerăm algoritmul în care read-ul returnează imediat valoarea din copia procesului MCS respectiv şi renunţă la broadcast.
Fie o execuţie în care valoarea iniţială a lui x este 0. Procesorul p i invocă o scriere a lui 1 în x şi execută un tbc – send pentru write. Mesajul difuzat ajunge la procesorul p j
care-şi actualizează copia sa locală a lui x pe 1, după care, sa spunem, execută un read pe x ce returnează valoarea nouă 1. Considerăm un al treilea procesor p k care nu a primit mesajul
de broadcast al lui pi şi are încă valoarea copiei lui x pe 0. Presupunem că pk citeşte x (după ce pj execută read, dar înainte de a primi broadcastul lui pi); clar va obţine valoarea 0.
Nici o permutare a acestor trei operaţii nu poate fi şi conformă cu specificaţia lui read/write şi să păstreze ordonarea după timpul real de apariţie a operaţiilor în .
Totuşi acest algoritm îndeplineşte condiţia de consistenţă secvenţială.
Algoritm de consistenţă secvenţială cu read local
(codul pentru procesorul pi)
Iniţial, copy[x] păstrează valoarea iniţială a obiectului partajat x pentru orice x.
1: atunci când apare readi(x):
2: enable returni(x, copy[x])
Lema 2: Pentru orice procesor pi şi orice obiecte partajate x şi y dacă un read r al obiectului y urmează după un write w asupra obiectului x în top()|i atunci readul r al copiei locale
a lui pi a lui y urmează după write-ul w al lui pi asupra copiei locale a lui x.
Din cele două leme rezultă.
Teoremă: Algoritmul precedent implementează un DMS secvenţial consistent cu read local.
Margini inferioare
Vom presupune că DSM a fost implementat în topul unui sistem de tip MP, point-to-point.
Adaptăm definiţia unei execuţii cu timpi (timed execution) pentru sistemul simulat, considerând că numai evenimentelor de intrare într-un nod li s-au asignat timpi,
celelalte evenimente din nod moştenind timpii evenimentelor de intrare în nod, care le-au declanşat.
În plus vom considera că:
– orice mesaj are o întârziere într-un interval [d-u,d], unde 0 < u d.
Pentru un MCS fixat şi o operaţie op vom nota cu t op timpul, în cazul cel mai nefavorabil, peste toate execuţiile admisibile, necesitat de operatia op (diferenţa dintre timpul real de
apariţie a răspunsului şi timpul real de apariţie al invocarii).
Teorema 1: Pentru orice MCS secvenţial consistent care asigură două obiecte partajate read/write, t read + twrite d.
Demonstraţie: Fie un MCS secvenţial consistent şi două obiecte partajate x şi y, iniţial 0 amândouă.
Există o execuţie admisibila 0 a lui MCS a.î.
top(0) = write0(x, 1) ack0(x) read0(y) return0(y,0)
şi write începe la timpul 0 iar read răspunde inaintea timpului d.
Presupunem, în plus, că orice mesaj trimis in 0 are intirzierea d. Rezulta ca nici un mesaj n-a fost primit de nici un nod la momentul de timp când read primeşte răspunsul.
Similar, există o execuţie admisibilă 1 a lui MCS a.î.
top(1) = write1(y, 1) ack1(y) read1(x) read1(x) return1(x,0)
write apare la timpul 0, răspunsul la read apare înaintea timpului d. Deci nici un mesaj n-a fost primit în nici un nod la momentul de timp când read primeşte răspunsul.
Vom construi o nouă execuţie care combină comportarea lui p0 în 0 (în care p0 scrie x şi citeşte y) cu comportamentul lui p 1 în 1 (scrie y şi citeşte x). Cum amândoua
operaţiile pe fiecare procesor se termină înainte de momentul d, p 0 nu ştie despre opearaţiile lui p1 şi invers. Deci procesoarele returnează aceeaşi valoare în pe care ele le fac în 0
(1). După timpul d în , se permite ca orice mesaj ce aşteaptă să fie livrat, dar este deja prea târziu pentru a mai afecta valorile returnate anterior. Clar, aceste valori sunt inconsistente
cu condiţia de consistenţă secvenţială.
Formal, fie i|i (i=0,1) şi i prefixul cel mai lung al lui i|i ce se termină înaintea timpului d, şi '=merge(0, 1). ' este prefixul unei execuţii admisibile . Dar top() este
exact secvenţa 3 din observaţia de după definiţia condiţiei de secvenţial consistenţă, care s-a văzut că nu-i secvenţial consistentă.
Observaţie: Marginea inferioară obţinută este valabilă în particular şi pentru MCS liniarizabil. În acest caz se poate spune chiar mai mult.
Teorema 2: Pentru orice MCS liniarizabil care asigură un obiect partajat read/write scris de două procesoare şi citit de un al treilea
0 u/2 t u u
write 2 r(x,2)
p0 u
Demonstraţie: Fie un MCS liniarizabil şi un obiect partajat x iniţial 0 care este citit de p0 şi scris de p1 şi p2. Presupunem prin reducere la absurd ca t
write 2.
Există o execuţie admisibilă a lui MCS a.î.
w(x,1)
p1
– top() = write (x, 1) ack (x) write (x, 2) ack (x) read (x) return (x,2).
: 1 1 2 2 0 0
– 1 2 w(x,2)
write (x,1) apare la timpul 0, write (x,2) apare la timpul u/2 şi read (x) apare la timpul u.
0
–
p 2 mesajelor în sunt: d din p la p ; d-u din p la p şi d-u/2 pentru orice altă pereche ordonată de procesoare.
întârzierile 1 2 2 1
t u
write 4
Demonstraţie: Fie un MCS liniarizabil şi x un obiect partajat, iniţial 0 care este scris de p0 şi citit de p1 şi p2. Presupunem prin reducere la absurd că t u
write 4
Ideea demonstraţiei este aceeaşi ca în teorema precedentă, numai că este ceva mai elaborată.
Fie K twrite/n . Considerăm o execuţie admisibilă a lui MCS în care toate întârzierile mesajelor sunt d-u/2 comţinând urmatoarele evenimente:
– La un moment intre
u
şi 4k 1 n , p face ack (x). ( 4k 1 n n twrite şi deci write-ul lui p se termină în acest interval).
0 0 0
4 4 4 4
n
– La momentul 2i , p1 face read1(x) 0 i 2k.
4
n
– La un moment între 2i şi 4i 1 n , p face return (x,V ) 0 i 2k.
1 1 2i
4 4
n
– La momentul 2i , p2 face read2(x) 0 i 2k.
4
Deci în top(), read-ul lui p1 al lui V0 precede write-ul lui p 0, read-ul lui p2 al lui V4k+1 urmează după write-ul lui p0, nu avem read-uri suprapuse şi ordinea citirilor valorilor din x este
V0, V1, V2,...,V4k+1.
Din liniarizabilitatea v0=0 şi V4k+1=1. j 0 j 4k a.î. Vj=0 şi Vj+1=1. Presupunem că j este par deci Vj este rezultatul unui read al lui p1.
Definim =shift(,<0,0,-n/4>) adică îl dăm înapoi pe p 2 cu n/4. este admisibilă, întrucât întârzierea mesajelor în p2 devine d-n, întârzierea mesajelor din p 2 devine d şi
celelalte întârzieri rămân neschimbate.
p0
w(x,1)
r(x,v1) r(x,v3) r(x,V4k+1)
p2
w(x,1)
p0
Correct Interaction: Pentru fiecare procesor pi, |i este şir alternat de invocări şi răspunsuri pereche, începind cu o invocare.
Nonfaulty Liveness: Orice invocare a unui procesor nedefect are un răspuns pereche.
Extended Liniarizability: Există o permutare a tuturor operaţiilor complete din (cele care au răspuns) şi a unei submulţimi a operaţiilor incomplete a.î.:
1. |o este legală pentru orice obiect o.
2. Dacă în , răspunsul la operaţia o1 apare înaintea invocării lui o2 , atunci o1 apare înaintea lui o2 în .
Vom studia dacă sistemul de comunicaţie C1 poate simula sistemul de comunicaţie C2 , unde C2 este un sistem cu memorie partajată cu un anumit tip de obiecte care
admite f căderi de procesoare, iar C1 este un sistem de comunicaţie (sau cu memorie partajată sau de tip MP) care admite f căderi de procesoare.
De fapt vom discuta cazul f=n-1, numit wait-free datorită definiţiei următoare echivalente.
O simulare a unui sistem de tip memorie partajată (fără defecţiuni) de către alt sistem de tip memorie partajată (tot fără defecţiuni) se numeşte wait-free dacă:
Pentru orice execuţie admisibilă a programului de simulare, daca ' este un prefix în care un procesor p i are o operaţie incompletă (există în ' o invocare din
mediul extern în pi fără un răspuns pereche) =', atunci |pi conţine răspunsul la această operaţie.
(Orice operaţie trebuie să fie capabilă să se termine în orice punct fără asistenţa altor procesoare, f=n-1; operaţiile pe procesoarele nedefecte se desfăşoară normal).
Observaţie: Condiţia de liniarizabilitate corespunde intuiţiei că operaţiile (de nivel înalt) apar ca fiind executate atomic într-un anumit moment în timpul execuţiei lor. Acest
moment se află undeva între invocarea şi răspunsul la operaţie şi se numeşte punct de liniarizabilitate. Un mod de a demonstra că o secvenţă este liniarizabilă este acela de a
asigna explicit fiecărei operaţii un punct de liniarizabilitate. Acesta va asigura ordinea temporală a operaţiilor ce nu se suprapun. Dacă se arată în plus că fiecare nod
returnează valoarea operatorului write cu punctul de liniarizabilitate cel mai recent ce precede operatorul read, am demonstrat că execuţia este liniarizabilă.
Vom prezenta în continuare următorul lanţ de simulări wait-free ale registrelor de tip read-write.
Un registru este single – reader (s – r) dacă există un singur procesor (cititorul) ce poate citi din el şi single – writer dacă există un singur procesor (scriitorul) ce
poate scrie în el, (s – w).
Simularea unui registru cu K valori, de tip s – r, s – w cu ajutorul unor registre s – r, s – w cu valori binare.
Pentru simplitate vom considera un singur registru R cu K valori şi două procesoare bine definite scriiorul şi cititorul. Pentru simularea lui R, vom considera un tablou de K
regiştri binari B[0..K-1]. Valoarea i va fi reprezentată de un 1 în componenta B[i] şi 0 în celelalte. Deci, valorile posibile ale lui R sunt {0,1,...,K-1}.
O operaţie read(R) va fi simulată prin scanarea vectorului B începând cu indicele 0 şi returnând prima componentă egală cu 1 (dacă există). O operaţie
write(R,v) va fi simulată punând 1 în B[v] şi şi ştergând componenta egală cu 1 ce corespunde valorii precedente a lui R.
Acest scenariu simplu funcţionează în cazul operaţiilor R şi W care nu se suprapun.
Dacă operaţiile cititorului sunt în ordine read(R,2) read(R,1)iar ale scriitorului write(R,1) write(R,2) atunci, o liniarizare a acestor operaţii va trebui să se
păstreze ordinea operaţiilor fiecărui procesor. Pentru a fi îndeplinit semantica registrului R va trebui ca write(R ,1) să apară înainte de read(R,2) pe cînd write(R,1) trebuie
să apară înainte de read(R,1) şi după read(R,2). Deci, sau ordinea operaţiilor read sau ordinea operaţiilor write trebuie schimbată. Algoritmul următor rezolvă această
problemă a inversiunilor "nou – vechi" ale valorilor.
Evident , algoritmul este wait-free; fiecare write execută cel mult K operaţii write de nivel jos iar fiecare read execută cel mult 2K-1 operaţii read de nivel jos.
Pentru a demonstra că algoritmul este corect trebuie să arătăm că orice execuţie edmisibilă este liniarizabilă, admite o permutare a operaţiilor de nivel înalt a.î. să se
păstreze ordinea operaţiilor care nu se suprapun şi în care orice read returnează valoarea ultimului write precedent.
Fixăm o execuţie admisibilă a algoritmului. Cum este corectă pentru sistemul de comunicaţie cu registre binare, operaţiile cu registre binare sunt liniarizabile. Deci
fiecare operaţie de nivel jos admite un punct de liniarizabilitate.
Asociem fiecărei configuraţii a execuţiei o "valoare actuală" a registrului multivaluat R: valoarea actuală a lui R este
k min i | i 0,1, , K 1 , B i 1, B 0 B i 1 0
În această definiţie valoarea lui B(i) este în raport cu punctele de liniarizabilitate ale operaţiilor de nivel jos.
Construim o liniarizare a lui . Punctele de liniarizabilitate al operaţiei write(R,k) este imediat după prima configuraţie din intervalul ei în care b[k]=1 şi B[0]=...=B[k-1]=0. La
terminare alui write(R,k) (atunci când apare ack(R)) aceasta este situaţia, dar punctul de liniarizabilitate poate fi mai înainte (dacă, de exemplu valoarea precedentă a lui R a fost mai
mare decât k, el este imediat după execuţia operaţiei de nivel jos B[k]:=1).
Punctul de liniarizabilitate a lui read(R) care se termină cu return(R,k) este imediat după prima configuraţie din intervalul ei, în care k este asignat variabilei locale v, (linia 3 sau 5).
Lema 1: Valoarea actuală a lui R rămâne fixă între două puncte de liniarizabilitate ale operaţiei write consecutive de nivel înalt.
Lema 2: Pentru fiecare operaţie read r (de nivel înalt) din 2, r returnează valoarea scrisă în R de operaţia write (de nivel înalt) cu ultimul punct de liniarizabilitate înaintea ultimului
punct de liniarizabilitate al lui r.
Mai multe procesoare (n) citesc din acelaşi registru. Vom permite existenţa unui singur scriitor pentru fiecare registru. Registrului R i se va asocia o colecţie de n registre s –
w, s – r Val[i] i=1,n , unul pentru fiecare cititor.
Un scenariu simplu de simulare:
write(R,v) va obliga cititorul să parcurgă toate cele n registre Val[i] şi să memoreze v în ele.
read(R) : cititorul i returnează valoarea din Val[i].
Din păcate această simulare este incorectă, există execuţii care nu sunt liniarizabile.
Exemplu:
Valoarea iniţială a registrului este 0. Scriitorul pw doreşte să scrie 1 în registru. Considerăm următoarea secvenţă de operaţii:
pw starteaza operaţia de write(R,1), scriind 1 în Val[1]
pw primeşte ack după scrierea lui 1 în Val[1], scrie 1 în Val[2] şi confirmă (ack) scrierea sa (de nivel superior).
Citirea lui p1 trebuie ordonată după scrierea lui p w şi citirea lui p 2 înaintea scrierii lui p w. Deci citirea lui p1 trebuie ordonată după citirea lui p 2.Totusi, citirea lui p1 apare înaintea citirii
lui p2.
Are loc următoarea teoremă:
Teoremă:
În orice simulare wait-free a unui registru s – w, m – r cu orice număr de registre s – r, s – w, cel puţin un cititor trebuie să scrie.
Soluţia pentru simulare va necesita ca cititorii să-şi scrie unii altora creind o ordine între ei. Înainte de a returna valoarea din read-ul său, un cititor îi va anunţa pe ceilalţi
aceasta valoare. Un cititor va citi valoarea scrisă de scriitor pentru el, dar şi valorile anunţate de ceilalţi cititori. Va alege cea mai recentă valoare pe care el a citit-o. Vom presupune că
registrele pot lua un număr nemărginit de valori. În această ipoteză, scriitorul poate asocia un număr întreg drept timestamp (a câta oară a scris) pentru fiecare valoare scrisă. În
continuare vom referi o valoare ca fiind perechea ce conţine o valoare reală şi numărul său de ordine.
Se vor folosi tablourile de registre s – r :
Val[i]: valoarea scrisă de pw pentru cititorul pi 1 i n
Report[i,j]: Val returnată de cea mai recentă operaţie read
executată de pi; scris de pi şi citit de pj 1 i,j n
Algoritmul este wait–free : fiecare operaţie simulată execută un număr fix de oparaţii de nivel jos (n pentru write şi 2n+1 pentru read).
Liniarizabilitatea se demonstreaza considerind o execuţie admisibilă oarecare si construind explicit permutarea .
Ideea algoritmului de simulare. Fiecare scriitor anunţă valoarea pe care intenţionează să o scrie, tuturor cititorilor (scriindu-le-o în registrul fiecăruia, registru de tip s – w, m
– r). Fiecare cititor citeşte toate valorile scrise de scriitori şi o alege pe cea mai recentă dintre ele. Depistarea valorii cea mai recentă se face cu ajutorul unui timestamp asociat fiecărei
valori. Spre deosebire de algoritmul precedent, timestamp-ul nu este generat de un singur procesor (unicul writer) ci de mai multe procesoare (toţi scriitorii posibili).
Scriitorii sunt p0,...,pm-1 iar cititorii sunt toate procesoarele p0,p1,...,pn-1. Se vor folosi tablourile de registri s – w, m – r :
TS[i] timestamp-ul vectorial al scriitorului pi 0 i m-1. Este scris de pi şi citit de toţi scriitorii.
Val[i] Ultima valoare scrisă de scriitorul pi 0 i m-1, împreună cu timestamp-ul vectorial asociat cu acea valoare. Scris de pi şi citit de toţi cititorii.
Iniţial, TS[i]=<0,...,0> şi
Val[i] este valoarea iniţială a lui R 0 i m-1
În cazul existenţei defecţiunilor, metoda descrisă pentru simularea memoriei partajate în topul unui sistem broadcast t.o., bazată pe aşteptarea confirmării din sistemul MP,
nu mai funcţionază. Atunci când există defecţiuni, un procesor nu poate fi sigur că va primi un mesaj trimis către el. Mai mult, nu poate aştepta o confirmare de la destinatar, pentru că
acesta poate pica.Vom folosi toate procesoarele ca extra – memorie pentru a implementa fiecare registru partajat (nu numai scriitorii şi cititorii acelui registru). Desigur, valorile sunt
acompaniate de numare de ordine. Metoda pe care o vom descrie funcţionează în ipoteza că numărul procesoarelor defecte f satisface f < n/2 (se poate demonstra că nu este posibilă o
simulare a unui registru read/write în topul unui sistem asincron MP dacă n2f).
Considerăm un registru particular ce va fi simulat. Când scriitorul doreşte să scrie o valoare în acest nou registru, incrementează un număr de ordine şi trimite un mesaj
<newval> conţinând noua valoare şi numărul de ordine, tuturor procesoarelor.
Fiecare primitor actualizează o variabilă locală cu această informaţie, dacă numărul de ordine asociat este mai mare decât valoarea curentă a numărului de ordine din
variabila locală; în orice eveniment receptorul trimite un mesaj de <ack>.
n n
Odată ce scriitorul primeşte <ack> de la cel puţin
2 1 procesoare el termină de scris. Cum n>2f 2 1 n f şi deci avem garanţia că va termina de
scris.Când cititorul doreşte să citească un registru, el trimite un mesaj de tip <request> tuturor procesoarelor. Fiecare procesor trimite înapoi un mesaj de tip <value> conţinând
n
informaţia curentă pe care o deţine. Odată ce cititorul primeşte <value> de la cel puţin
2 1 procesoare, el returnează valoarea cu cel mai mare număr de secvenţă dintre cele
primite.
n
Comunicarea cu
2 1 procesoare garantează un quorum pentru fiecare operaţie read sau write. Cum în aceste quorum-uri avem majoritatea procesoarelor, va exista
un procesor în intersecţia oricărui quorum al unui read cu orice quorum al unui write. Acesta garantează faptul că ultima valoare scrisă va fi obţinută.
Pentru a evita problemele potenţiale legate de asincronie, şi scriitorul şi cititorul menţin o variabilă locală status [0..n-1] pentru a guverna comunicarea; status[j] conţine una
din valorile:
not-sent: mesajul pentru cea mai recentă operaţie n-a fost încă trimis lui pj (pentru că pj n-a confirmat un mesaj anterior)
not-acked: mesajul pentru cea mai recentă operaţie a fost trimis lui pj dar n-a fost confirmat de pj
În plus, fiecare procesor are un numărător num-acks cu care-şi numără răspunsurile primite în timpul operaţiei curente.