中科大算法分析与设计分布式算法复习知识点

一、分布式算法

1.1 导论

并行处理与分布式处理的区别

  • 并行处理目标:使用所有处理器执行一个大任务。
  • 分布式处理具有更高程度的不确定性和行为的独立性,每个处理器都有自己独立的任务。

分布式系统作用

  • 共享资源
  • 改善性能
  • 提高可靠性

模型:

  • 异步共享存储模型:用于紧耦合机器
  • 异步msg传递模型:用于松散耦合机器及广域网
  • 同步msg传递模型:msg延迟上界已知,系统执行划分为轮执行,是异步系统的特例

1.2 消息传递中的基本算法

每个处理器\(p_i\)可以模型化为一个具有状态集\(Q_i\)的状态机

初始状态\(Q_i\)包含一个特殊的初始状态子集,每个inbuf必须为空,outbuf不一定为空。

转换函数:将可访问状态的inbuf转换到outbuf

配置:分布式系统上某点整个算法的全局状态

事件计算事件传递事件

执行:配置和事件交错的序列。

需满足安全性条件和活跃性条件。

  1. 安全性:某个性质在每次执行的每个可达配置里都成立。“坏事从不发生”

  2. 活跃性:某个性质在每次执行的某些可达配置里成立。“最终某个好事发生”

满足安全性条件称为执行。同时满足活跃性条件称为容许执行

1.2.1 转移系统与安全性和活跃性

转移系统\(三元组 Triple \ S=(C,\rightarrow,I)\)\(C\)是配置集,\(I\)是初始配置的一个子集,\(\rightarrow\)是配置集上的二元转移关系。

可达\(\exist \ 序列 \gamma_0 \rightarrow \gamma_1 \rightarrow \cdots \rightarrow \gamma_k = \delta,且\forall 0 \le i \le k-1,\gamma_i \rightarrow \gamma_{i+1},则称\delta是可达的\)

安全性\(S=(C,\rightarrow,I),断言/性质P总是成立\)

\(\{P\} \rightarrow \{Q\}:\forall \gamma \rightarrow \delta, if \ P(\gamma),then \ Q(\delta)\),即如果P在转移前成立,则Q在转移后成立

定义:\(P是S的不变式,\forall \gamma \in I,P(\gamma)成立且\{P\}\rightarrow\{P\},P总是成立\),P为安全性条件

定理:如果P是S的不变式,那么对S的每次执行的每一配置P都成立

活跃性:在算法的每次执行的某些配置中,P为真,且最终P为真。

判断方法:

  • 范函数
  • 无死锁 / 系统正常终止

范函数\(转移系统S和断言P,f:配置集C\rightarrow良基集\omega\)

良基集\(偏序集(\omega,<)是良基的,当且仅当不存在无穷递减序列,即存在最小值\)

1.2.2 系统

异步系统

异步msg传递时间和一个处理器的两个相继步骤之间的时间无上界

执行片段:一个有限或无限的序列,由配置事件交替组成

执行:从初始配置开始的执行片段

调度执行中的事件序列

容许执行:某个处理器有无限个计算事件(指处理器没有出错,并不是无限步),每个发送的msg都最终被传递。

同步系统

处理器按轮执行,一轮里,每个处理器可以发送一个msg到邻居,每个处理器一接受到msg就计算。

:配置和事件序列可以划分为不相交的轮,每轮由一个传递事件一个计算事件组成。

同步系统与异步系统的区别:

  1. 无错的同步系统中,算法的执行只取决于初始配置
  2. 异步系统中,算法可能有不同的执行(不代表不同的结果)

1.2.3 复杂性度量

消息复杂度:所有容许执行上发送消息总数的最大值

时间复杂度

  • 同步系统:最大轮数
  • 异步系统:所有计时容许执行中直到终止的最大时间

1.2.4 生成树上的广播和敛播

生成树ST:具有共同顶点不出现回路的子图。

最小生成树MST:所有边权值和最小的生成树。

广播

基本步骤

  1. \(p_r\)发送\(M\)给所有的孩子。
  2. 某节点收到父节点的\(M\)时,发送\(M\)给自己的所有孩子。
upon receiving no msg:
	if i=r then
        send <M> to all children;
		terminates;
upon receiving <M> from P_j:
	send <M> to all children;
	terminates;

消息复杂度\(O(n)\)

时间复杂度\(O(h),h为生成树高度\)

敛播

基本步骤

  1. 每个叶子节点发送消息给双亲
  2. 每个非叶节点等待所有孩子的消息,之后再发消息给双亲

消息复杂度\(O(n)\)

时间复杂度\(O(h),h为生成树高度\)

1.2.5 构造生成树

Flooding算法

消息复杂度:\(O(2m-(n-1))\)

事件复杂度:\(O(D),D为网直径\)

基本思想

  1. 根节点发送消息\(M\)给所有邻居

  2. \(P_i\)收到来自\(P_j\)的消息\(M\)且是第一次收到消息\(M\)时,\(P_i\)发送\(<parent>\)\(P_j\)\(P_i\)向其他向自己发送消息\(M\)的节点发送\(<reject>\)\(P_i\)\(P_j\)以外的邻居发送消息\(M\)

  3. \(P_i\)收到\(P_j的<parent>\)消息,则表明\(P_i是P_j的父节点\)

Code for Pi (0<=i<=n-1)
初值:parent=nil;集合children和other均为空集

upon receiving no message:
    if i=r and parent=nil then { //根尚未发送M
    	send M to all neighbors;
    	parent:=i;} //根的双亲置为自己
upon receiving M from neighbor pj:
    if parent=nil then { //pi此前未收到过M,M是pi收到的第1个msg
    	parent:=j;
    	send <parent> to pj; //pj是pi的双亲
    	send M to all neighbors except pj;
    }else //pj不可能是pi的双亲,pi收到的M不是第1个msg
    	send <reject> to pj;
upon receiving <parent> from neighbor pj:
    children:=children∪{ j }; //pj是pi的孩子,将j加入孩子集
    if children∪other包含了除parent外的所有邻居 then terminate;
upon receiving <reject> from neighbor pj:
    other:=other∪{ j }; //将j加入other,通过非树边发送的msg。
    if children∪other包含了除parent外的所有邻居 then terminate

异步模型中,构造以\(p_r\)为根的生成树

同步模型中,构造的一定是BFS

在异步系统中无法生成BFS,因为消息传递无上界,最坏可能出现单链。

1.2.6 指定根构造DFS生成树

基本思想

  1. 选定根节点\(P_r\),向某一邻接节点发送消息\(M\)
  2. \(P_i\)收到\(P_j\)的消息\(M\)是第一个来自邻接节点的消息时,认定\(P_j\)为其双亲,并向之后给自己发消息\(M\)的节点发送\(<reject>\)
  3. \(P_i\)向未发过消息的邻居中任选一个发送消息\(M\),并等待回复
  4. \(P_i\)向所有邻居发过消息,则\(P_i\)终止
Code for Pi (0<=i<=n-1)
初值:
parent = nil;
children = Φ;
unexplored = Pi's neighbors

upon receiving no message:
	if i=r and parent=nil then { //当Pi为根且未发送M
    	parent:=i; // 将parent设置为自身
     	任意 Pj ∈ unexplored
      	将 Pj 从 unexplored 中删去
    	send M to Pj;}//endif
upon receiving M from neighbor Pj:
    if parent=nil then { //pi此前未收到过M
    	parent:=j;
       	将 Pj 从 unexplored 中删去
        if unexplored!=Φ then {
           任意 Pk ∈ unexplored
      	将 Pk 从 unexplored 中删去
    	send M to Pk;
        }else send <parent> to parent;
    }else send <reject> to pj; //当Pj已访问过
upon receiving <parent> or <reject> from neighbor pj:
    if received <parent> then add j to children; // Pj是Pi的孩子
    if unexplored = Φ then { //Pi的邻居均已访问
    	if parent!=i then send <parent> to parent; //Pi非根,返回至双亲
     	terminate; //以Pi为根的DFS子树已构造好
    }else{ //选择Pi未访问过的邻居访问
    	任意 Pk ∈ unexplored
      	将 Pk 从 unexplored 中删去
    	send M to Pk;
    }

消息复杂度,时间复杂度:\(O(m)\)

1.2.7 不指定根构造DFS生成树

与领导者选举问题类似,为破对称问题

基本思想

  1. 每个结点均可自发唤醒,试图构造一根以自己为根的DFS生成树。若两棵DFS树试图链接同一节点(未必同时),选择根ID较大的DFS树加入

  2. 每个结点设置一个leader变量,即为当前DFS树的根ID

  3. 结点自发唤醒时,将自己的leader发送给某一邻居

Code for Pi (0<=i<=n-1)
初值:
parent = nil;
leader = 0;
children = Φ;
unexplored = Pi's neighbors

upon receiving no message: // 自主唤醒
    if parent=nil then { //若非空,则Pi在某子树上,则Pi失去竞选机会
    	leader:=id; parent:=i; // 将parent设置为自身
     	任意 Pj ∈ unexplored
      	将 Pj 从 unexplored 中删去
    	send <leader> to Pj;}//endif
upon receiving <new_id> from neighbor Pj:
    if leader<new_id then { //将Pi所在树合并到Pj所在树中
    	leader:=new_id; parent:=j;
     	unexplored:=all neighbors of Pi except Pj; //重置未访问邻居集
      	if unexplored!=Φ then { //原Pi所在DFS树修改各自id
           	任意 Pk ∈ unexplored
            	将 Pk 从 unexplored 中删去
    		send <leader> to Pk;
      	}else send <parent> to parent;
    }else if leader=new_id then send <already> to Pj;
upon receiving <parent> or <already> from neighbor pj:
    if received <parent> then add j to children;
    if unexplored = Φ then { //Pi的邻居均已访问
    	if parent!=i then send <parent> to parent; //Pi非根,返回至双亲
     	else terminate as root of the DFS tree; //以Pi为根的DFS子树已构造好
    }else{ //选择Pi未访问过的邻居访问
    	任意 Pk ∈ unexplored
      	将 Pk 从 unexplored 中删去
    	send <leader> to Pk;
    }

一个具有m条边,n个节点的网络,自发启动的节点由p个,ID值最大的启动时间为t。

消息复杂度:\(O(pn^2)\)

时间复杂度:\(O(t+m)\)

1.3 环上选举算法

匿名的:环中处理器没有唯一的标识符,每个处理器具有相同的状态机

一致性、均匀的:不知道处理器的数目

解释一致性和非一致性:

算法一:向右转发n步msg,然后终止。 => non_uniform

算法二:向右转发msg,直到msg发送者收到msg。 => uniform

在一个匿名的、一致性算法中,所有处理器只有一个状态机。

在一个匿名的、非一致性算法中,每个n值对应一种状态机。

  • 同步环系统不存在匿名的、一致性的领导者选举算法
  • 异步环系统不存在匿名的领导者选举算法

1.3.1 异步环

开调度:在算法A的一次调度中,有一条边上无消息传递,则为开边。

(开调度未必是容许的调度,是容许执行的一个有限前缀)

异步环上的选举算法有消息复杂度下界\(O(nlg \ n)\)

\(O(n^2)的算法\)

基本思想

  1. 每个处理器发送一个标识符msg给左邻居,然后等着接收右邻居的msg。
  2. 每个处理器收到msg时,若收到的id大于自身,则向左邻居转发。
  3. 若处理器收到自己的标识符id,则宣布自己是leader,并发终止msg给左邻居,然后终止
  4. 若处理器收到终止msg,则向左转发,然后终止

核心思想:处理器向左邻居发送标识符msg,通过比对id确定leader,只有最大的id消息会回到他自己。

Code for Pi (0<=i<=n-1)
初值:asleep=true; id = i;

While (receiving no message) do
  (1) if asleep do
      (1.1) asleep = false
      (1.2) send <id> to left-negihbor
   end if
End while
While (receiving <i> from right-neighbor) do
 (1) if id < <i> then send <i> to left-neighbor
       end if
 (2) if id = <i> then
      (2.1) send <Leader,i> to left-neighbor
      (2.2) terminates as Leader
   end if
End while
While (receiving <Leader,j> from right-neighbor) do
 (1) send <Leader,j> to left-neighbor
 (2) terminates as non-Leader
End while

消息复杂度:\(O(n^2)\)

\(O(nlg(n))\)的算法

k邻居:据某一处理器距离不超过k的处理器,左边有k个,右边有k个,共有\(2k+1\)个。

基本思想

  1. 阶段0:每个节点向两个1-邻居发送id消息,若邻居的id小于此消息则回复reply,否则吞没。若一个节点收到两个邻居的reply,则自认为leader,并进入阶段1。
  2. 阶段\(l\):在上一阶段成为leader的处理器,继续发送id消息给\(2^l\)邻居,若收到来自左右两个方向的reply,则自认为leader。
  3. 若收到自己的消息,则终止。

核心思想:第\(l\)阶段一个处理器试图成为其\(2^l\)-邻居的临时leader。只有在\(l-th\)阶段成为leader的处理器才能继续\((l+1)-th\)阶段。

Code for Pi (0<=i<=n-1)
初值:asleep=true;

upon receiving no msg:
      if  asleep then{
      	asleep:=false;//每个结点唤醒后不再进入此代码
       	send <probe,id, 0, 0> to left and right;
      }
upon receiving <probe, j, l,d> from left_or_right (resp, right):
    if(j=id) then //收到自己id终止,省略发终止msg
      	send <leader,id> to left neighbour;
       	terminate as the leader;
    if(j>id) and (d<2^l) then //向前转发probe msg
    	send <probe, j, l, d+1> to right_or_left (resp, left)
    if(j>id) and (d≥2^l) then //到达最后一个邻居仍未没收
    	send <reply, j, l > to left_or_right (resp, right) // 回答
    // 若j<id, 则没收probe消息
upon receiving <reply ,j , l> from left (resp, right):
    if j≠id then // 判断是否转发到初始点
    	send <reply, j, l> to right (resp, left); //转发reply
    else //j=id时,Pi已收到一个方向的回答msg
    	if already received <reply, j, l> from right (resp, left) then //也收到另一方向发回的reply
 			send <probe, id, l+1, 0> to left and right; //Pi是phase l的临时leader,继续下一阶段
upon receiving <leader, idj> from right:
    send <leader, idj> to left;
    terminate as nonleader; 

阶段\(k\),临时leader树最多为\(\frac{n}{2^k+1}\),启动的msg数目最多为\(4*2^k\)

消息复杂度:\(O(nlg \ n)\)

1.3.2 同步环

消息复杂度上界\(O(n)\),下界\(O(nlg \ n)\)

上界\(O(n)\)

  • 非均匀:要求环中所有节点开始于同一轮
  • 均匀:节点可以开始于不同轮

证明上界的非均匀算法

必须知道环大小n,所有节点开始于同一轮

假定id最小的为leader

基本思想:按阶段运行,每个阶段由n轮组成。阶段\(i\)中,处理器id也为\(i\)的选举为leader,然后终止算法。

各节点互不知道彼此的id值。

消息复杂度:恰有n个消息被发送,\(O(n)\)

证明上界的均匀算法

无需知道环大小

一个处理可以:自发唤醒或被动唤醒

基本思想

  1. 源于\(id=i\)节点的msg,在被转发前延迟\(2^i-1\)轮。
  2. 每个自发唤醒的节点绕环发送一个唤醒msg,且无延迟
  3. 若节点在启动前收到唤醒msg,则该节点只作转发操作。

只有自发唤醒的节点,才有可能选为leader

初值:asleep=true;waiting = ф;R = 计算事件中接收msg的集合;s = ф;//the msg to be sent

if asleep then {
    asleep:=false;
    if R = Φ then { // pi未收到过msg,属于自发唤醒
        min:=id;   // 参与选举
        s:=s+{<id>}; // 准备发送
    }else{ //已收到过msg,但此前未启动,被唤醒故Pi不参与
    	min:=∞; //选举,置min为∞ ,使其变为relay结点
    	// relay:=true; ?
    }
}
for each <m> in R do {// 处理完收到的m后相当于从R中删去
    if m < min then { // 收到的id较小时通过
    	become not elected;  // Pi未被选中
    	// 可用relay控制使转发节点不延迟?
    	将<m>加入waiting且记住m何时加入; // m加入延迟转发
    	min:=m;
    } // if m > min then it is swallowed
    if m=id then become elected; // Pi被选中
} //endfor
for each <m> in waiting do
    if <m> 是在2^m-1轮之前接收的 then
    	将<m>从waiting中删去并加入S
send S to left;

发送的消息:

  • 第一类:第一阶段的msg(唤醒msg
  • 第二类:最终leader的msg进入自己的第二阶段之前发送的第二阶段msg(其他节点发的)
  • 第三类:最终leader的msg进入自己的第二阶段之后发送的第二阶段msg(包括leader发的)

第一类总数最多\(n\)

第二类总数最多\(n\)

第三类总数最多\(2n\)

消息复杂度:\(O(4n)\)

有限制的下界\(O(nlg \ n)\)

序等价:两个环\(x_0,x_1,\cdots,x_n-1\)\(y_0,y_1,\cdots,y_n-1\)\(x_i < x_j,当且仅当y_i<y_j\)

若一算法只与环上标识符之间的相对次序相关,而与具体id无关,则该算法一定只是基于标识符的比较

对于每个\(n\ge8,n是2的方幂\),存在大小为n的环\(S_n\),使得基于比较的同步leader选举算法A,在容许执行里发送的msg数目为\(O(nlg \ n)\)

构造\(S_n\)

  1. 定义大小为n的环\(R_n^{rev}\),令\(P_i\)的id为\(rev(i):i的二进制逆序列\)
  2. 将环划分为长度为\(j,j为2的方幂\)的连续片段,则这些片段是序等价的。
  3. 片断数为\(\frac{n}{j}\)

1.4 计算模型

计算模型的复杂性:

  • 系统由并发部件构成
  • 无全局时钟
  • 必须捕捉部件可能的失效

对策:

  • 因果关系
  • 一致状态
  • 全局状态

为什么分布式系统缺乏全局系统状态

  1. 非即时通信(传播延时,资源竞争,丢失msg重发)

  2. 相对性影响(大多数计算机的实际时钟存在漂移,时钟同步依旧是一个问题

  3. 中断(不可能在同一时刻观察一个分布式系统的全局状态)

次序\(e_1 < e_2:事件e_1发生在e_2之前\)

定序

  • 发生在同一节点上的事件满足全序,\(e_1<_pe_2\)
  • \(e_1\)发送消息,\(e_2\)接收消息,则\(e_1<_me_2\)

Happens-before关系\(<_H\),一种偏序关系

并发事件:两个事件不能由 \(<_H\) 定序

有时将Happens-before关系描述为一个有向无环图

image-20220103152117876

如何将H关系的偏序关系转变成全序关系:

  • 在有向无环图DAG上的拓扑排序
  • Lamport算法

1.4.1 Lamport时间戳

基本思想

  • 事件e有附加时间戳:e.TS
  • 节点有局部时间戳:my_TS
  • msg有附加时间戳:m.TS
  • 节点执行事件时,将自己的时间戳赋给该事件。
  • 节点发送msg时,将自己的时间戳给所有的msg。
  • 接收消息时,本地时间戳更新为最大值
  • 发送消息时,使用本地时间戳打标签
Initially: my_TS = 0;
On event e:
    if(e == 接收消息m) then
    	my_TS = max(m.TS, my_TS)); // 取msg时间戳和节点时间戳较大值
     my_TS++;
     e.TS = my_TS; // 给事件e打时间戳
     if(e == 发送消息m) then
     	m.TS = my_TS; // 给消息打时间戳

每一时间的时间戳大于前驱时间的时间戳

问题:不同的时间可能有相同的时间戳(并发事件)

改进算法:使用节点地址作为时间戳低位,如\(1.1,2.2\)。标号后按字典序得到全序关系。

问题:无法通过时间戳判定两个事件是否有因果关系

1.4.2 向量时间戳

向量时间戳VT\(e.VT是个数组,e.VT[i]=k表示再节点i上,事件e之前有k个事件(可包括自己)\)

基本思想

  • 节点有局部向量时间戳:my_VT

  • 事件e有向量时间戳:e.VT

  • msg有向量时间戳:m.VT

Initially: my_VT=[0,0,,,0];
On event e:
    if(e == 接收消息m) then
		for i= 1 to M do // 向量时间戳的每个分量只增不减
    		my_VT[i] = max(m.VT[i], my_VT[i]);
    my_VT[self]++; // self是本节点的名字
    e.VT = my_VT; // 给事件e打时间戳
    if(e == 发送消息m) then
    	m.VT = my_VT; // 给消息m打时间戳

向量时间戳比较:

e1.VT = (5,4,1,3)
e2.VT = (3,6,4,2)
e3.VT = (0,0,1,3)
  1. e1和e2之间没有完全大于或完全小于的关系,则e1和e2是并发的。

  2. e3在因果序上先于e1

1.4.3 因果通信

处理器不能选择msg到达的时间,但是可以抑制过早到达的msg。

如TCP中的FIFO通信。

基本思想

  • 抑制从P发送的消息,知道可以断定没有其他消息早于m发生
  • 在每个节点上:
    • \(earliest[1...M]:不同节点当前能够传递的消息时间戳的下界\)
    • \(blocked[1...M]P:阻塞队列数组,每个分量是一个队列\)
Causal Msg Delivery
Definition:
    时间戳lk:
    	使用Lamport时间戳,lk=1;
     	使用向量时间戳,lk=(0,,,0,1,0,,,0),第k位为1;
Initially:
    earliest[k] = lk,k=1,,,M // 不同节点当前能传递的消息时间戳的下界
    blocked[k] = {},k=1,,,M // 阻塞队列置空
        
On the receipt of msg <m> from node P:
    delivery_list = {}
    if (blocked[p]==空) then
    	earliest[p] = m.timestamp;
     	blocked[p].push_back(m); // 处理收到的消息
    //处理阻塞队列
    while(∃k使blocked[k]非空 && 
    	对i=1,,,M(除k和self外) not_earliest(earliest[i],earliest[k],i) )
     	// 没有消息比k更早到
    {
    	将blocked[k]队头元素m’出队,且加入到delivery_list;
    	if (blocked[k]!=空) then
    		earliest[k] = m’.timestamp;
    	else
     		increment earliest[k] by lk
    }
    deliver the msgs in delivery_list; // 按因果序

可能发生死锁(一节点上时间不发送你要的msg)

该因果通信算法常用于组播

本文来自博客园,作者:小陆斑比,转载请注明原文链接:https://www.cnblogs.com/bamlubi/p/15763704.html

原文地址:https://www.cnblogs.com/bamlubi/p/15763704.html