Presentation is loading. Please wait.

Presentation is loading. Please wait.

Universality of Consensus

Similar presentations


Presentation on theme: "Universality of Consensus"— Presentation transcript:

1 Universality of Consensus
Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit 1

2 Art of Multiprocessor Programming
Turing Computability 1 1 A mathematical model of computation Computable = Computable on a T-Machine Art of Multiprocessor Programming

3 Shared-Memory Computability
cache shared memory Model of asynchronous concurrent computation Computable = Wait-free/Lock-free computable on a multiprocessor Art of Multiprocessor Programming

4 Art of Multiprocessor Programming
Consensus Hierarchy 1 Read/Write Registers, Snapshots… 2 getAndSet, getAndIncrement, … . ∞ compareAndSet,… Art of Multiprocessor Programming

5 Art of Multiprocessor Programming
Who Implements Whom? 1 Read/Write Registers, Snapshots… no 2 getAndSet, getAndIncrement, … no . no ∞ compareAndSet,… Art of Multiprocessor Programming

6 Art of Multiprocessor Programming
Hypothesis 1 Read/Write Registers, Snapshots… yes? 2 getAndSet, getAndIncrement, … yes? . yes? ∞ compareAndSet,… Art of Multiprocessor Programming

7 Theorem: Universality
Consensus is universal From n-thread consensus build a Wait-free Linearizable n-threaded implementation Of any sequentially specified object Art of Multiprocessor Programming

8 Art of Multiprocessor Programming
Proof Outline A universal construction From n-consensus objects And atomic registers Any wait-free linearizable object Not a practical construction But we know where to start looking … Art of Multiprocessor Programming

9 Art of Multiprocessor Programming
Like a Turing Machine This construction Illustrates what needs to be done Optimization fodder Correctness, not efficiency Why does it work? (Asks the scientist) How does it work? (Asks the engineer) Would you like fries with that? (Asks the liberal arts major) Art of Multiprocessor Programming

10 A Generic Sequential Object
public interface SeqObject { public abstract Response apply(Invocation invoc); } The sequential object in has an initial state, and each call to \mApply{} has an invocation as its input. The invocation is a description of the invoked method and its arguments. The \mApply{} call applies the invoked method to the object by modifying the generic object's state and returning an appropriate output as its response. For example, a stack would have its invocations be either a push with the appropriate argument or a pop with a void argument. The response of the push would be void and the response of the pop would be the last element pushed. Art of Multiprocessor Programming

11 A Generic Sequential Object
public interface SeqObject { public abstract Response apply(Invocation invoc); } The sequential object in has an initial state, and each call to \mApply{} has an invocation as its input. The invocation is a description of the invoked method and its arguments. The \mApply{} call applies the invoked method to the object by modifying the generic object's state and returning an appropriate output as its response. For example, a stack would have its invocations be either a push with the appropriate argument or a pop with a void argument. The response of the push would be void and the response of the pop would be the last element pushed. Push:5, Pop:null Art of Multiprocessor Programming

12 Art of Multiprocessor Programming
Invocation public class Invoc { public String method; public Object[] args; } Art of Multiprocessor Programming

13 Art of Multiprocessor Programming
Invocation public class Invoc { public String method; public Object[] args; } Method name Art of Multiprocessor Programming

14 Art of Multiprocessor Programming
Invocation public class Invoc { public String method; public Object[] args; } Arguments Art of Multiprocessor Programming

15 A Generic Sequential Object
public interface SeqObject { public abstract Response apply(Invocation invoc); } The sequential object in has an initial state, and each call to \mApply{} has an invocation as its input. The invocation is a description of the invoked method and its arguments. The \mApply{} call applies the invoked method to the object by modifying the generic object's state and returning an appropriate output as its response. For example, a stack would have its invocations be either a push with the appropriate argument or a pop with a void argument. The response of the push would be void and the response of the pop would be the last element pushed. Art of Multiprocessor Programming

16 A Generic Sequential Object
public interface SeqObject { public abstract Response apply(Invocation invoc); } The sequential object in has an initial state, and each call to \mApply{} has an invocation as its input. The invocation is a description of the invoked method and its arguments. The \mApply{} call applies the invoked method to the object by modifying the generic object's state and returning an appropriate output as its response. For example, a stack would have its invocations be either a push with the appropriate argument or a pop with a void argument. The response of the push would be void and the response of the pop would be the last element pushed. OK, 4 Art of Multiprocessor Programming

17 Art of Multiprocessor Programming
Response public class Response { public Object value; } Art of Multiprocessor Programming

18 Art of Multiprocessor Programming
Response public class Response { public Object value; } Return value Art of Multiprocessor Programming

19 Universal Concurrent Object
public interface SeqObject { public abstract Response apply(Invocation invoc); } A universal concurrent object is linearizable to the generic sequential object The sequential object in has an initial state, and each call to \mApply{} has an invocation as its input. The invocation is a description of the invoked method and its arguments. The \mApply{} call applies the invoked method to the object by modifying the generic object's state and returning an appropriate output as its response. For example, a stack would have its invocations be either a push with the appropriate argument or a pop with a void argument. The response of the push would be void and the response of the pop would be the last element pushed. Art of Multiprocessor Programming 19 19

20 Start with Lock-Free Universal Construction
First Lock-free: infinitely often some method call finishes. Then Wait-Free: each method call takes a finite number of steps to finish We will start by implementing a lock-free universal construction, and only then move on to develop it into a wait-free solution Art of Multiprocessor Programming

21 Art of Multiprocessor Programming
Naïve Idea Consensus object stores reference to cell with current state Each thread creates new cell computes outcome, tries to switch pointer to its outcome Sadly, no … consensus objects can be used once only Can’t have one pointer have consensus does on it many times by the same thread. If students ask, trying to have threads have a new decide object in a node and then go back and update the pointer with a simple write based on the decision will also not work because slow Writers may erase what fast ones did. Our solution is actually along these lines, but it has a chain of nodes in each of which the pointer is only set once which is why it works… Art of Multiprocessor Programming

22 Art of Multiprocessor Programming
Naïve Idea enq deq Art of Multiprocessor Programming

23 Decide which to apply using consensus
Naïve Idea Concurrent Object enq Decide which to apply using consensus deq No good. Each thread can use consensus object only once ? head Art of Multiprocessor Programming

24 Not clear how to reuse or reread …
Once is not Enough? Queue based consensus public T decide(T value) { propose(value); Ball ball = queue.deq(); if (ball == Ball.RED) return proposed[i]; else return proposed[1-i]; } Solved one-shot 2-consensus. Not clear how to reuse or reread … Art of Multiprocessor Programming

25 Improved Idea: Linked-List Representation
Each node contains a fresh consensus object used to decide on next operation deq tail enq enq enq Art of Multiprocessor Programming

26 Universal Construction
Object represented as Initial Object State A Log: a linked list of the method calls Traverse the list and compute the response by applying all the method calls from the initial state. Art of Multiprocessor Programming

27 Universal Construction
Object represented as Initial Object State A Log: a linked list of the method calls New method call Find end of list Atomically append call Compute response by replaying log Traverse the list and compute the response by applying all the method calls from the initial state. Art of Multiprocessor Programming

28 Art of Multiprocessor Programming
Basic Idea Use one-time consensus object to decide next pointer Because we are using a new node every time, we have a consensus object that is used only one time. But as we will explain later, it cannot be read, so we need the actual pointer to be a regular memory location. Because All threads write the same value to this location, there will not be a problem. Art of Multiprocessor Programming

29 Art of Multiprocessor Programming
Basic Idea Use one-time consensus object to decide next pointer All threads update actual next pointer based on decision OK because they all write the same value Because we are using a new node every time, we have a consensus object that is used only one time. But as we will explain later, it cannot be read, so we need the actual pointer to be a regular memory location. Because All threads write the same value to this location, there will not be a problem. Art of Multiprocessor Programming

30 Art of Multiprocessor Programming
Basic Idea Threads use one-time consensus object to decide which node goes next Threads update actual next field to reflect consensus outcome OK because they all write the same value Challenges Lock-free means we need to worry what happens if a thread stops in the middle Because we are using a new node every time, we have a consensus object that is used only one time. But as we will explain later, it cannot be read, so we need the actual pointer to be a regular memory location. Because All threads write the same value to this location, there will not be a problem. Art of Multiprocessor Programming

31 Art of Multiprocessor Programming
Basic Data Structures public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus<Node> decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus<Node>() seq = 0; } Art of Multiprocessor Programming

32 Standard interface for class whose objects are totally ordered
Basic Data Structures public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus<Node> decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus<Node>() seq = 0; } Standard interface for class whose objects are totally ordered Art of Multiprocessor Programming

33 Art of Multiprocessor Programming
Basic Data Structures public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus<Node> decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus<Node>() seq = 0; } the invocation Art of Multiprocessor Programming

34 (next method applied to object)
Basic Data Structures public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus<Node> decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus<Node>() seq = 0; } Decide on next node (next method applied to object) Art of Multiprocessor Programming

35 Basic Data Structures Traversable pointer to next node
public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus<Node> decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus<Node>() seq = 0; } Traversable pointer to next node (needed because you cannot repeatedly read a consensus object) Art of Multiprocessor Programming

36 Art of Multiprocessor Programming
Basic Data Structures public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus<Node> decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus<Node>() seq = 0; } Seq number Art of Multiprocessor Programming

37 Create new node for this method invocation
Basic Data Structures Create new node for this method invocation public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus<Node> decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus<Node>() seq = 0; } Art of Multiprocessor Programming

38 Ptr to cell w/highest Seq Num
Universal Object Seq number, Invoc next node tail decideNext (Consensus Object) 1 2 3 4 head Ptr to cell w/highest Seq Num Art of Multiprocessor Programming

39 All threads repeatedly modify head…back to where we started?
Universal Object node tail 1 2 3 4 All threads repeatedly modify head…back to where we started? head Art of Multiprocessor Programming

40 The Solution 1 2 3 4 … node tail head Make head an array
Find head by finding Max of nodes referenced by head array node tail 1 2 3 4 head We need to locate the current head of the log. Unfortunately, we cannot use a simple consensus object because it must be updated repeatedly, and we assume that consensus objects can only be accessed once by each thread. Instead, we create a distributed structure of the kind used in the Bakery algorithm of Chapter~\ref{chapter:mutex}. We represent the head pointer \aHead{} as an $n$-entry array, where \aHead{i} is the last node in the list that thread $i$ has observed, understanding that many of the observed head array entries may be outdated due to concurrency. Initially all entries point to the same dummy node pointed to by the \fTail{}. The returned value for the head is determined by finding the node with the maximum sequence number among the nodes read in the head array. The method $\max()$ in Figure~\ref{figure:node} will return this maximal value, the node in the log with possibly the highest \fSeq{} sequence number, but no smaller than the maximum in the array before $max()$ was called. Ref to node at front i Thread i updates location i Make head an array Art of Multiprocessor Programming

41 Art of Multiprocessor Programming
Universal Object public class Universal { private Node[] head; private Node tail = new Node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail } Art of Multiprocessor Programming

42 Art of Multiprocessor Programming
Universal Object public class Universal { private Node[] head; private Node tail = new Node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail } Head Pointers Array Art of Multiprocessor Programming

43 Tail is a sentinel node with
Universal Object public class Universal { private Node[] head; private Node tail = new Node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail } Tail is a sentinel node with sequence number 1 Art of Multiprocessor Programming

44 Initially head points to tail
Universal Object public class Universal { private Node[] head; private Node tail = new Node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail } Initially head points to tail Art of Multiprocessor Programming

45 Art of Multiprocessor Programming
Find Max Head Value public static Node max(Node[] array) { Node max = array[0]; for (int i = 1; i < array.length; i++) if (max.seq < array[i].seq) max = array[i]; return max; } This work like in the bakery algorithm. Art of Multiprocessor Programming

46 Art of Multiprocessor Programming
Find Max Head Value public static Node max(Node[] array) { Node max = array[0]; for (int i = 0; i < array.length; i++) if (max.seq < array[i].seq) max = array[i]; return max; } Traverse the array Art of Multiprocessor Programming

47 Compare the seq nums of nodes pointed to by the array
Find Max Head Value public static Node max(Node[] array) { Node max = array[0]; for (int i = 0; i < array.length; i++) if (max.seq < array[i].seq) max = array[i]; return max; } Compare the seq nums of nodes pointed to by the array Art of Multiprocessor Programming

48 Return node with maximal sequence number
Find Max Head Value public static Node max(Node[] array) { Node max = array[0]; for (int i = 0; i < array.length; i++) if (max.seq < array[i].seq) max = array[i]; return max; } Return node with maximal sequence number Art of Multiprocessor Programming

49 Universal Application Part I
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Part 1 of the universal application. Art of Multiprocessor Programming

50 Universal Application Part I
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Apply has invocation as input and returns the appropriate response Art of Multiprocessor Programming

51 Universal Application Part I
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } my ID Art of Multiprocessor Programming

52 Universal Application Part I
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } My method call Art of Multiprocessor Programming

53 Universal Application Part I
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } As long as I have not been threaded into list Art of Multiprocessor Programming

54 Universal Application Part I
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Head of list to which we will try to append Art of Multiprocessor Programming

55 Universal Application Part I
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Propose next node Art of Multiprocessor Programming

56 Universal Application
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Set next field to consensus winner Notice that the next pointer could have already been written by another node, in which case the thread is overwriting the locations with the same value. Art of Multiprocessor Programming

57 Universal Application Part I
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Set seq number (indicating node was appended) Notice that the setting could be by a node other than the one whose method call the winning node represents. Art of Multiprocessor Programming

58 Universal Application Part I
public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } add to head array so new head will be found Notice that the setting could be by a node other than the one whose method call the winning node represents. Art of Multiprocessor Programming

59 Part 2 – Compute Response
Red’s method call tail deq() null enq( ) enq( ) return Private copy of object Art of Multiprocessor Programming

60 Universal Application Part 2
... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); In this part of the code we compute the response. Art of Multiprocessor Programming

61 Universal Application Part II
... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); Compute result by sequentially applying method calls in list to a private copy To compute my response. Art of Multiprocessor Programming

62 Universal Application Part II
... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); To compute my response. Start with copy of sequential object Art of Multiprocessor Programming

63 Universal Application Part II
... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); To compute my response. new method call appended after tail Art of Multiprocessor Programming

64 Universal Application Part II
... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); To compute my response. While my method call not linked … Art of Multiprocessor Programming

65 Universal Application Part II
... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); To compute my response. Apply current node’s method Art of Multiprocessor Programming

66 Universal Application Part II
... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); To compute my response. Return result after my method call applied Art of Multiprocessor Programming

67 Art of Multiprocessor Programming
Correctness List defines linearized sequential history Thread returns its response based on list order Our construction is correct, that is, it is a linearizable implementation of the sequential object, because the log is immutable and each \mApply{} call can be linearized at the point in which the consensus call adding it to the log was decided. Why is our construction lock-free? We first note that the ``true'' head of the log, the latest node agreed on as the new head, is always added to the head array within a finite number of steps from the moment of decision. This claim follows because the node's predecessor must already appear in the head array, and any node repeatedly attempting to add a new node will repeatedly run the $\max$ function on the head array. It will detect this predecessor, run consensus on its \fDecideNext{}, then update all its fields including the sequence number, and finally add the decided node to its entry of the head array. Thus, the new ``true'' head node is always eventually recorded in the head array. Therefore, the only way a thread can continue to spin indefinitely, failing to add its own node to the log, is if other threads repeatedly succeed in appending their own nodes to the log, continuously changing the new head node. This kind of starvation can occur only if the other nodes are continually completing their associated invocations, implying that the construction is lock-free. Art of Multiprocessor Programming

68 Art of Multiprocessor Programming
Lock-freedom Lock-free because A thread moves forward in list Can repeatedly fail to win consensus on “real” head only if another succeeds Consensus winner adds node and completes within a finite number of steps Our construction is correct, that is, it is a linearizable implementation of the sequential object, because the log is immutable and each \mApply{} call can be linearized at the point in which the consensus call adding it to the log was decided. Why is our construction lock-free? We first note that the ``true'' head of the log, the latest node agreed on as the new head, is always added to the head array within a finite number of steps from the moment of decision. This claim follows because the node's predecessor must already appear in the head array, and any node repeatedly attempting to add a new node will repeatedly run the $\max$ function on the head array. It will detect this predecessor, run consensus on its \fDecideNext{}, then update all its fields including the sequence number, and finally add the decided node to its entry of the head array. Thus, the new ``true'' head node is always eventually recorded in the head array. Therefore, the only way a thread can continue to spin indefinitely, failing to add its own node to the log, is if other threads repeatedly succeed in appending their own nodes to the log, continuously changing the new head node. This kind of starvation can occur only if the other nodes are continually completing their associated invocations, implying that the construction is lock-free. Art of Multiprocessor Programming 68 68

69 Wait-free Construction
Lock-free construction + announce array Stores (pointer to) node in announce If a thread doesn’t append its node Another thread will see it in array and help append it How do we turn a lock-free algorithm into a wait-free one? The full wait-free algorithm appears in Figure~\ref{figure:universal}. We need to guarantee that every thread completes its \mApply{} within some finite number of steps, that is, no thread starves. To guarantee this property, threads making progress must help more unfortunate threads to complete their calls. The ``helping'' methodology we will show here in a universal context occurs in a more specialized form in many wait-free algorithms. To allow helping, each thread must share with other threads the details of the \mApply{} call that it is trying to complete. We therefore add to our algorithm an $n$-element \aAnnounce{} array, where \aAnnounce{i} is the node thread $i$ is currently trying to append to the list. Initially all entries point to the dummy node, which has a sequence number of $1$. We will say that a thread $i$ \emph{announces} a node when it stores the node in the \aAnnounce{} array at index $i$, and denote the announced node as being in \aAnnounce{i} for a given thread $i$. Art of Multiprocessor Programming

70 Art of Multiprocessor Programming
Helping “Announcing” my intention Guarantees progress Even if the scheduler hates me My method call will complete Makes protocol wait-free Otherwise starvation possible Art of Multiprocessor Programming

71 Wait-free Construction
Ref to cell thread i wants to append announce i tail 1 2 3 4 head i Art of Multiprocessor Programming

72 Art of Multiprocessor Programming
The Announce Array public class Universal { private Node[] announce; private Node[] head; private Node tail = new node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail; announce[j] = tail }; Art of Multiprocessor Programming

73 New field: announce array
The Announce Array public class Universal { private Node[] announce; private Node[] head; private Node tail = new node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail; announce[j] = tail }; New field: announce array Art of Multiprocessor Programming

74 All entries initially point to tail
The Announce Array public class Universal { private Node[] announce; private Node[] head; private Node tail = new node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail; announce[j] = tail }; All entries initially point to tail Art of Multiprocessor Programming

75 Art of Multiprocessor Programming
A Cry For Help public Response apply(Invoc invoc) { int i = ThreadID.get(); announce[i] = new Node(invoc); head[i] = Node.max(head); while (announce[i].seq == 0) { // while node not appended to list } Art of Multiprocessor Programming

76 Announce new method call, asking help from others
A Cry For Help public Response apply(Invoc invoc) { int i = ThreadID.get(); announce[i] = new Node(invoc); head[i] = Node.max(head); while (announce[i].seq == 0) { // while node not appended to list } Announce new method call, asking help from others Art of Multiprocessor Programming

77 Art of Multiprocessor Programming
A Cry For Help public Response apply(Invoc invoc) { int i = ThreadID.get(); announce[i] = new Node(invoc); head[i] = Node.max(head); while (announce[i].seq == 0) { // while node not appended to list } Look for end of list Art of Multiprocessor Programming

78 Main loop, while node not appended (either by me or helper)
A Cry For Help public Response apply(Invoc invoc) { int i = ThreadID.get(); announce[i] = new Node(invoc); head[i] = Node.max(head); while (announce[i].seq == 0) { // while node not appended to list } While the node I announced is not appended continue main loop. Main loop, while node not appended (either by me or helper) Art of Multiprocessor Programming

79 Art of Multiprocessor Programming
Main Loop Non-zero sequence # means success Art of Multiprocessor Programming

80 Art of Multiprocessor Programming
Main Loop Non-zero sequence # means success Thread keeps helping append nodes Art of Multiprocessor Programming

81 Art of Multiprocessor Programming
Main Loop Non-zero sequence # means success Thread keeps helping append nodes Until its own node is appended Art of Multiprocessor Programming

82 Art of Multiprocessor Programming
Main Loop while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1) % n]; if (help.seq == 0) prefer = help; else prefer = announce[i]; Art of Multiprocessor Programming

83 Keep trying until my cell gets a sequence number
Main Loop while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1) % n]; if (help.seq == 0) prefer = help; else prefer = announce[i]; Keep trying until my cell gets a sequence number Art of Multiprocessor Programming

84 Art of Multiprocessor Programming
Main Loop while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1) % n]; if (help.seq == 0) prefer = help; else prefer = announce[i]; Possible end of list Art of Multiprocessor Programming

85 Art of Multiprocessor Programming
Main Loop while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1) % n]; if (help.seq == 0) prefer = help; else prefer = announce[i]; Whom do I help? Art of Multiprocessor Programming

86 Art of Multiprocessor Programming
Altruism Choose a thread to “help” Art of Multiprocessor Programming

87 Art of Multiprocessor Programming
Altruism Choose a thread to “help” If that thread needs help Try to append its node Otherwise append your own Art of Multiprocessor Programming

88 Art of Multiprocessor Programming
Altruism Choose a thread to “help” If that thread needs help Try to append its node Otherwise append your own Worst case Everyone tries to help same pitiful loser Someone succeeds Art of Multiprocessor Programming

89 Art of Multiprocessor Programming
Help! When last node in list has sequence number k Art of Multiprocessor Programming

90 Art of Multiprocessor Programming
Help! When last node in list has sequence number k All threads check … Whether thread k+1 mod n wants help If so, try to append her node first Art of Multiprocessor Programming

91 Art of Multiprocessor Programming
Help! First time after thread k+1 announces No guarantees Art of Multiprocessor Programming

92 Art of Multiprocessor Programming
Help! First time after thread k+1 announces No guarantees After n more nodes appended Everyone sees that thread k+1 wants help Everyone tries to append that node Someone succeeds Art of Multiprocessor Programming

93 Art of Multiprocessor Programming
Sliding Window Lemma After thread A announces its node No more than n other calls Can start and finish Without appending A’s node Art of Multiprocessor Programming

94 So all see and help append 4
Helping So all see and help append 4 Thread 4: Help me! Max head +1 = n+4 announce 4 1 2 3 tail n+2 n+3 As if there is a window that slides and the node at the edge if the window gets helped. head Art of Multiprocessor Programming

95 The Sliding Help Window
announce 3 4 help 3 help 4 1 2 3 tail n+2 n+3 As if there is a window that slides and the node at the edge if the window gets helped. Notice that the window slides along the sequence numbers, and the helping is done to the thread ids. head Art of Multiprocessor Programming

96 In each main loop iteration pick another thread to help
Sliding Help Window while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1) % n]; if (help.seq == 0) prefer = help; else prefer = announce[i]; In each main loop iteration pick another thread to help Art of Multiprocessor Programming

97 Help if help required, but otherwise it’s all about me!
Sliding Help Window Help if help required, but otherwise it’s all about me! while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1) % n]; if (help.seq == 0) prefer = help; else prefer = announce[i]; Art of Multiprocessor Programming

98 Rest is Same as Lock-free
while (announce[i].seq == 0) { Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Art of Multiprocessor Programming

99 Rest is Same as Lock-free
while (announce[i].seq == 0) { Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Call consensus to attempt to append Art of Multiprocessor Programming

100 Rest is Same as Lock-free
while (announce[i].seq == 0) { Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } cache consensus result for later use Art of Multiprocessor Programming

101 Rest is Same as Lock-free
while (announce[i].seq == 0) { … Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Tell world that node is appended Art of Multiprocessor Programming

102 Art of Multiprocessor Programming
Finishing the Job Once thread’s node is linked … Art of Multiprocessor Programming

103 Art of Multiprocessor Programming
Finishing the Job Once thread’s node is linked… The rest same as lock-free algorithm Art of Multiprocessor Programming

104 Art of Multiprocessor Programming
Finishing the Job Once thread’s node is linked … The rest same as lock-free algorithm Compute result by sequentially applying list’s method calls to a private copy of the object starting from the initial state Art of Multiprocessor Programming

105 Art of Multiprocessor Programming
Then Same Part II ... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != announce[i]){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); In this part of the code we compute the response. Art of Multiprocessor Programming

106 Universal Application Part II
... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); Return result after applying my method To compute my response. Art of Multiprocessor Programming

107 Shared-Memory Computability
Universal Construction 10011 Wait-free/Lock-free computable = Solving n-consensus Art of Multiprocessor Programming

108 Swap (getAndSet) not Universal
public class RMWRegister { private int value; public boolean getAndSet(int update) { int prior = value; value = update; return prior; } Art of Multiprocessor Programming

109 Swap (getAndSet) not Universal
public class RMWRegister { private int value; public boolean getAndSet(int update) { int prior = value; value = update; return prior; } Consensus number 2 Art of Multiprocessor Programming

110 Not universal for ≥3 threads
Swap (getAndSet) not Universal public class RMWRegister { private int value; public boolean getAndSet(int update) { int prior = value; value = update; return prior; } Not universal for ≥3 threads Art of Multiprocessor Programming

111 CompareAndSet is Universal
public class RMWRegister { private int value; public boolean compareAndSet(int expected, int update) { int prior = value; if (value == expected) { value = update; return true; } return false; }} Art of Multiprocessor Programming

112 CompareAndSet is Universal
public class RMWRegister { private int value; public boolean compareAndSet(int expected, int update) { int prior = value; if (value == expected) { value = update; return true; } return false; }} Consensus number ∞ Art of Multiprocessor Programming

113 CompareAndSet is Universal
public class RMWRegister { private int value; public boolean compareAndSet(int expected, int update) { int prior = value; if (value == expected) { value = update; return true; } return false; }} Universal for any number of threads Art of Multiprocessor Programming

114 On Older Architectures
IBM 360 testAndSet (getAndSet) NYU UltraComputer getAndAdd (fetchAndAdd) Neither universal Except for 2 threads Art of Multiprocessor Programming 114 114

115 On Newer Architectures
Intel x86, Itanium, SPARC compareAndSet (CAS, CMPXCHG) Alpha AXP, PowerPC Load-locked/store-conditional All universal For any number of threads Trend is clear … Art of Multiprocessor Programming 115 115

116 Practical Implications
Any architecture that does not provide a universal primitive has inherent limitations You cannot avoid locking for concurrent data structures … Art of Multiprocessor Programming

117 Shared-Memory Computability
Universal Object 10011 Wait-free/Lock-free computable = Threads with methods that solve n-consensus Art of Multiprocessor Programming 117 117

118 Art of Multiprocessor Programming
Veni, Vidi, Vici We saw how to define concurrent objects Art of Multiprocessor Programming 118

119 Art of Multiprocessor Programming
Veni, Vidi, Vici We saw how to define concurrent objects We discussed computational power of machine instructions Art of Multiprocessor Programming 119

120 Art of Multiprocessor Programming
Veni, Vidi, Vici We saw how to define concurrent objects We discussed computational power of machine instructions Next use these foundations to understand the real world Art of Multiprocessor Programming 120

121 Art of Multiprocessor Programming
          This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License. You are free: to Share — to copy, distribute and transmit the work to Remix — to adapt the work Under the following conditions: Attribution. You must attribute the work to “The Art of Multiprocessor Programming” (but not in any way that suggests that the authors endorse you or your use of the work). Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license. For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to Any of the above conditions can be waived if you get permission from the copyright holder. Nothing in this license impairs or restricts the author's moral rights. Art of Multiprocessor Programming


Download ppt "Universality of Consensus"

Similar presentations


Ads by Google