Zegar każdego węzła w systemie jest niezależny.
Czy można by po prostu zsynchronizować zegary?
Rozwiązanie: czas wirtualny, odzwierciedlający tylko logicznie zachodzące w systemie zdarzenia i stanowiący tylko aproksymację czasu rzeczywistego.
Każdemu zdarzeniu przypisujemy etykietę zegarową
Ustalamy relację porządku między etykietami zegarowymi
Formalnie...
Zegarem logicznym systemu rozproszonego nazywamy pewien abstrakcyjny mechanizm, który każdemu zdarzeniu $E\in {\mathcal E}$, przyporządkuje wartość $\mathcal{T}( E )$ z przeciwdziedziny ${\mathcal {Y}}$, tak by zachować logiczne zależności między zdarzeniami.
$$ \forall E, E^{\prime} \in \mathcal{E} :: E \mapsto E^{\prime} \implies \mathcal{T}(E) < \mathcal{T}(E^{\prime})$$
Jeżeli lokalne zdarzenie $E_i^k$ w procesie $P_i$ poprzedza lokalnie inne zdarzenie lokalne $E_i^l$, to znacznik czasowy dla $E_i^k$ musi być mniejszy niż $E_i^l$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Zegarem logicznym systemu rozproszonego nazywamy pewien abstrakcyjny mechanizm, który każdemu zdarzeniu $E\in {\mathcal E}$, przyporządkuje wartość $\mathcal{T}( E )$ z przeciwdziedziny ${\mathcal {Y}}$, tak by zachować logiczne zależności między zdarzeniami.
Niech wartościami zegara będą pojedyncze liczby ("skalary") naturalne.
W implementacji oznaczać to będzie, że stworzymy zmienną $clock_i$ typu integer
Jeżeli lokalne zdarzenie $E_i^k$ w procesie $P_i$ poprzedza lokalnie inne zdarzenie lokalne $E_i^l$, to znacznik czasowy dla $E_i^k$ musi być mniejszy niż $E_i^l$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Jeżeli lokalne zdarzenie $E_i^k$ w procesie $P_i$ poprzedza lokalnie inne zdarzenie lokalne $E_i^l$, to znacznik czasowy dla $E_i^k$ musi być mniejszy niż $E_i^l$
Przy każdym zdarzeniu lokalnym, które uznamy za ważne, $$clock_i = clock_i + step$$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Przy każdym zdarzeniu lokalnym, które uznamy za ważne, $$clock_i = clock_i + step$$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Każdą wiadomość opatrzymy timestampem (etykietą zegarową), $m.ts$. Wysłanie jest ważnym wydarzeniem, więc zwiększamy $clock_i$ i potem $m.ts = clock_i$. Przy odbiorze w $P_j$ wartość zegara $clock_j$ nie może się cofnąć i nie może być mniejsza od zegara $clock_i$ po wysłaniu.
Przy każdym zdarzeniu lokalnym, które uznamy za ważne, $$clock_i = clock_i + step$$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Każdą wiadomość opatrzymy timestampem (etykietą zegarową), $m.ts$. Wysłanie jest ważnym wydarzeniem, więc zwiększamy $clock_i$ i potem $m.ts = clock_i$. Przy odbiorze w $P_j$ wartość zegara $clock_j$ nie może się cofnąć i nie może być mniejsza od zegara $clock_i$ po wysłaniu.
$$clock_j = max(clock_j, m.ts) + 1$$
struct message is < blob data , int ts >
local int ~~clock_i~~
when ~~{P}_i~~ tries to send a message ~~M = \left\langle data, ts \right\rangle~~ to ~~{P}_{j}~~ do
~~clock_i~~ := ~~clock_i + 1~~
~~M.ts~~ := ~~clock_i~~
send ~~M~~ to ~~{P}_j~~
end when
when at ~~{P}_i~~ arrives a message ~~M = \left\langle data, ts \right\rangle~~ from ~~{P}_{j}~~ do
~~clock_i~~ := ~~max\left(clock_i, M.ts\right) + 1~~
deliver ~~M~~ to ~~{P}_i~~
end when
$$(E\mapsto E')\Rightarrow ({\mathcal {T}}(E)<{\mathcal {T}}(E')$$ ale $$({\mathcal {T}}(E)<{\mathcal {T}}(E'))\not \Rightarrow (E\mapsto E')$$
Zegarem logicznym systemu rozproszonego nazywamy pewien abstrakcyjny mechanizm, który każdemu zdarzeniu $E\in {\mathcal E}$, przyporządkuje wartość $\mathcal{T}( E )$ z przeciwdziedziny ${\mathcal {Y}}$, tak by zachować logiczne zależności między zdarzeniami.
Niech wartościami zegara będą wektory liczb naturalnych.
W implementacji oznaczać to będzie, że stworzymy $n$-elementową zmienną $vclock_i$ typu tablicowego integer
Kiedy w takim razie uznamy, że $vclock_i < vclock_j$?
Kiedy w takim razie uznamy, że $vclock_i[k] < vclock_j[l]$?
$$vClock_{i}=vClock_{j}\Leftrightarrow \forall k :: vClock_{i}[k]=vClock_{j}[k]$$ $$vClock_{i}\neq vClock_{j}\Leftrightarrow \exists k :: vClock_{i}[k]\neq vClock_{j}[k]$$
Kiedy w takim razie uznamy, że $vclock_i[k] < vclock_j[l]$?
$$vClock_{i}=vClock_{j}\Leftrightarrow \forall k :: vClock_{i}[k]=vClock_{j}[k]$$ $$vClock_{i}\neq vClock_{j}\Leftrightarrow \exists k :: vClock_{i}[k]\neq vClock_{j}[k]$$
$$ vClock_{i}\leq vClock_{j}\Leftrightarrow \forall k :: vClock_{i}[k]\leq vClock_{j}[k]$$
$$vClock_{i}=vClock_{j}\Leftrightarrow \forall k :: vClock_{i}[k]=vClock_{j}[k]$$ $$vClock_{i}\neq vClock_{j}\Leftrightarrow \exists k :: vClock_{i}[k]\neq vClock_{j}[k]$$
$$ vClock_{i}\leq vClock_{j}\Leftrightarrow \forall k :: vClock_{i}[k]\leq vClock_{j}[k]$$
$$ vClock_{i} <vClock_{j}\Leftrightarrow \forall k :: vClock_{i}[k]\leq vClock_{j}[k]\land vClock_{i}\neq vClock_{j}$$
$$vClock_{i}=vClock_{j}\Leftrightarrow \forall k :: vClock_{i}[k]=vClock_{j}[k]$$ $$vClock_{i}\neq vClock_{j}\Leftrightarrow \exists k :: vClock_{i}[k]\neq vClock_{j}[k]$$
$$ vClock_{i}\leq vClock_{j}\Leftrightarrow \forall k :: vClock_{i}[k]\leq vClock_{j}[k]$$
$$ vClock_{i} <vClock_{j}\Leftrightarrow \forall k :: vClock_{i}[k]\leq vClock_{j}[k]\land vClock_{i}\neq vClock_{j}$$
$$vClock_{i}\nless vClock_{j}\Leftrightarrow \neg (\forall k :: vClock_{i}[k]\leq vClock_{j}[k]\land vClock_{i}\neq vClock_{j}$$ $$vClock_{i} || vClock_{j}\Leftrightarrow vClock_{i}\nless vClock_{j}\land vClock_{j}\nless vClock_{i}$$
Wektor A | Wektor B |
---|---|
1 | 2 |
3 | 3 |
2 | 4 |
2 | 2 |
Wektor A | Wektor B |
---|---|
1 | 2 |
4 | 3 |
2 | 4 |
2 | 2 |
Jeżeli lokalne zdarzenie $E_i^k$ w procesie $P_i$ poprzedza lokalnie inne zdarzenie lokalne $E_i^l$, to znacznik czasowy dla $E_i^k$ musi być mniejszy niż $E_i^l$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Jeżeli lokalne zdarzenie $E_i^k$ w procesie $P_i$ poprzedza lokalnie inne zdarzenie lokalne $E_i^l$, to znacznik czasowy dla $E_i^k$ musi być mniejszy niż $E_i^l$
Przy każdym zdarzeniu lokalnym, które uznamy za ważne, $$vclock_i[i] = vclock_i[i] + step$$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Przy każdym zdarzeniu lokalnym, które uznamy za ważne, $$vclock_i[i] = vclock_i[i] + step$$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Przy każdym zdarzeniu lokalnym, które uznamy za ważne, $$vclock_i[i] = vclock_i[i] + step$$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Przy każdym wysłaniu $vclock_i[i] = vclock_i[i] + step$, a następnie $m.vts = vclock_i$
Przy każdym zdarzeniu lokalnym, które uznamy za ważne, $$vclock_i[i] = vclock_i[i] + step$$
Wysłanie wiadomości musi być postrzegane jako wcześniejsze niż odebranie tej samej wiadomości, tzn wartości zegara dla wysłania wiadomości muszą być mniejsze niż dla odebrania tejże wiadomości.
Przy każdym wysłaniu $vclock_i[i] = vclock_i[i] + step$, a następnie $m.vts = vclock_i$
Przy każdym odbiorze $vclock_i[i] = vclock_i[i] + step$, a następnie $$\forall k\neq i : vclock_i[k]=max\left(vclock_i[k], m.vts[k]\right)$$
struct message is < blob data , int vts >
local int ~~vclock_i[n] := \left\langle 0,0 \ldots 0\right\rangle~~
when ~~{P}_i~~ tries to send a message ~~M = \left\langle data, vts \right\rangle~~ to ~~{P}_{j}~~ do
~~vclock_i[i]~~ := ~~vclock_i[i] + 1~~
~~M.vts~~ := ~~vclock_i~~
send ~~M~~ to ~~{P}_j~~
end when
when at ~~{P}_i~~ arrives a message ~~M = \left\langle data, vts \right\rangle~~ from ~~{P}_{j}~~ do
~~vclock_i [i]~~ := ~~vclock_i[i] + 1~~
for all ~~k~~ in ~~0 \ldots n~~ do
if ~~k \neq i~~ then ~~vclock_i[k]~~ := ~~max\left(vclock_i[k], M.vts[k]\right)~~
end for
deliver ~~M~~ to ~~{P}_i~~
end when
$$(E\mapsto E')\iff ({\mathcal {T}}(E)<{\mathcal {T}}(E')$$
$$(E || E')\iff ({\mathcal {T}}(E) || {\mathcal {T}}(E')$$
Implementacja, uruchomienie programu i pomierzenie czasów rzeczywistych to ZUUUUOOO:
Chcemy miarę teoretyczną, niezależną od języka, kompilatorów, środowiska.
Miara ma pozwalać na szybkie porównywanie mocnych i silnych stron algorytmów.
Równocześnie będziemy mieli świadomość, że miara ta to złożoność teoretyczna, tj dla określonego środowiska lub dla konkretnych wymagań algorytm teoretycznie gorszy może okazać się w rzeczywistości bardziej pożądany.
Interesują nas algorytmy wymagające współpracy między procesami rozproszonymi rozwiązujące problemy inne, niż wymagające np. dużej mocy obliczeniowej albo przepustowości. To jest: chodzi nam np. o dostęp do sekcji krytycznej, rozgłaszanie itd.
Pozostaje rozważyć, co właściwie i jak chcemy liczyć.
Pamiętamy : to założenia mające sens dla algorytmów tego rodzaju, jakie będziemy rozważać!