Presentation is loading. Please wait.

Presentation is loading. Please wait.

Linked Lists: Locking, Lock-Free, and Beyond … The Art of Multiprocessor Programming Spring 2007.

Similar presentations


Presentation on theme: "Linked Lists: Locking, Lock-Free, and Beyond … The Art of Multiprocessor Programming Spring 2007."— Presentation transcript:

1 Linked Lists: Locking, Lock-Free, and Beyond … The Art of Multiprocessor Programming Spring 2007

2 © Herlihy-Shavit 20072 Last Lecture: Spin-Locks CS Resets lock upon exit spin lock critical section...

3 © Herlihy-Shavit 20073 Today: Concurrent Objects AddingThreads Should not lower throughput –Contention effects –Mostly fixed by Queue locks Should increase throughput –Not possible if inherently sequential –Surprising things are parallelizable

4 © Herlihy-Shavit 20074 Coarse-Grained Synchronization Each method locks the object –Avoid contention using queue locks –Easy to reason about In simple cases –Standard Java model Synchronized blocks and methods So, are we done?

5 © Herlihy-Shavit 20075 Coarse-Grained Synchronization Sequential bottleneck –All threads “stand in line” Adding more threads –Does not improve throughput –Struggle to keep it from getting worse So why even use a multiprocessor? –Well, some apps inherently parallel …

6 © Herlihy-Shavit 20076 This Lecture Introduce four “patterns” –Bag of tricks … –Methods that work more than once … For highly-concurrent objects Goal: –Concurrent access –More threads, more throughput

7 © Herlihy-Shavit 20077 First: Fine-Grained Synchronization Instead of using a single lock.. Split object into –Independently-synchronized components Methods conflict when they access –The same component … –At the same time

8 © Herlihy-Shavit 20078 Second: Optimistic Synchronization Object = linked set of components Search without locking … If you find it, lock and check … –OK, we are done –Oops, try again Evaluation –cheaper than locking –mistakes are expensive

9 © Herlihy-Shavit 20079 Third: Lazy Synchronization Postpone hard work Removing components is tricky –Logical removal Mark component to be deleted –Physical removal Do what needs to be done

10 © Herlihy-Shavit 200710 Fourth: Lock-Free Synchronization Don’t use locks at all –Use compareAndSet() & relatives … Advantages –Robust against asynchrony Disadvantages –Complex –Sometimes high overhead

11 © Herlihy-Shavit 200711 Linked List Illustrate these patterns … Using a list-based Set –Common application –Building block for other apps

12 © Herlihy-Shavit 200712 The Evolution of Concurrent List-based Sets Course Grained Fine Lock- Coupling Course Optimistic Fine Optim- istic Lock- free Lazy Fine Optim- istic

13 © Herlihy-Shavit 200713 Set Interface Unordered collection of objects No duplicates Methods –Add() a new object –Remove() an object –Test if set Contains() object

14 © Herlihy-Shavit 200714 List-Based Sets public interface Set { public boolean add(Object x); public boolean remove(Object x); public boolean contains(Object x); }

15 © Herlihy-Shavit 200715 List-Based Sets public interface Set { public boolean add(Object x); public boolean remove(Object x); public boolean contains(Object x); } Add object to set

16 © Herlihy-Shavit 200716 List-Based Sets public interface Set { public boolean add(Object x); public boolean remove(Object x); public boolean contains(Object x); } Remove object from set

17 © Herlihy-Shavit 200717 List-Based Sets public interface Set { public boolean add(Object x); public boolean remove(Object x); public boolean contains(Object x); } Is object in set?

18 © Herlihy-Shavit 200718 List Node public class Node { public Object object; public int key; public Node next; }

19 © Herlihy-Shavit 200719 List Node public class Node { public Object object; public int key; public Node next; } Object of interest

20 © Herlihy-Shavit 200720 List Node public class Node { public Object object; public int key; public Node next; } Usually hash code

21 © Herlihy-Shavit 200721 List Node public class Node { public Object object; public int key; public Node next; } Reference to next node

22 © Herlihy-Shavit 200722 The List-Based Set abc Ordered + Sentinel nodes (min & max possible keys)

23 © Herlihy-Shavit 200723 Reasoning about Concurrent Objects Invariant –Property that always holds Established by –True when object is created –Truth preserved by each method Each step of each method

24 © Herlihy-Shavit 200724 Specifically … Invariants preserved by –add() –remove() –contains() Most steps are trivial –Usually one step tricky –Often linearization point

25 © Herlihy-Shavit 200725 Interference Proof that invariants preserved works only if –methods considered –are the only modifiers Language encapsulation helps –List nodes not visible outside class

26 © Herlihy-Shavit 200726 Interference Freedom from interference neeeded even for removed nodes –Some algorithms traverse removed nodes –Careful with malloc() & free() ! Garbage-collection helps here

27 © Herlihy-Shavit 200727 Abstract Data Types Concrete representation Abstract Type –{a, b} ab

28 © Herlihy-Shavit 200728 Abstract Data Types Meaning of rep given by abstraction map –S( ) = {a,b} a b

29 © Herlihy-Shavit 200729 Rep Invariant Which concrete values are meaningful? –Sorted? Duplicates? Rep invariant –Characterizes legal concrete reps –Preserved by methods –Relied on by methods

30 © Herlihy-Shavit 200730 Blame Game Rep invariant is a contract Suppose –add() leaves behind 2 copies of x –remove() removes only 1 Which one is incorrect?

31 © Herlihy-Shavit 200731 Blame Game Suppose –add() leaves behind 2 copies of x –remove() removes only 1 Which one is incorrect? –If rep invariant says no duplicates add() is incorrect –Otherwise remove() is incorrect

32 © Herlihy-Shavit 200732 Rep Invariant (partly) Sentinel nodes –tail reachable from head Sorted, no duplicates

33 © Herlihy-Shavit 200733 Abstraction Map S(head) = –{ x | there exists a such that A reachable from head and a.object = x –}

34 © Herlihy-Shavit 200734 Sequential List Based Set a c d b a b c Add() Remove()

35 © Herlihy-Shavit 200735 Course Grained Locking a b d c Simple but hotspot + bottleneck

36 © Herlihy-Shavit 200736 Coarse-Grained Locking Easy, same as synchronized methods –“One lock to rule them all …” Simple, clearly correct –Deserves respect! Works poorly with contention –Queue locks help –But bottleneck still an issue

37 © Herlihy-Shavit 200737 Fine-grained Locking Requires careful thought –“Do not meddle in the affairs of wizards, for they are subtle and quick to anger” Split object into pieces –Each piece has own lock –Methods that work on disjoint pieces need not exclude each other

38 © Herlihy-Shavit 200738 Hand-over-Hand locking abc

39 © Herlihy-Shavit 200739 Hand-over-Hand locking abc

40 © Herlihy-Shavit 200740 Hand-over-Hand locking abc

41 © Herlihy-Shavit 200741 Hand-over-Hand locking abc

42 © Herlihy-Shavit 200742 Hand-over-Hand locking abc

43 © Herlihy-Shavit 200743 Removing a Node abcd remov e b

44 © Herlihy-Shavit 200744 Removing a Node abcd remov e b

45 © Herlihy-Shavit 200745 Removing a Node abcd remov e b

46 © Herlihy-Shavit 200746 Removing a Node abcd remov e b

47 © Herlihy-Shavit 200747 Removing a Node acd remov e b

48 © Herlihy-Shavit 200748 Removing a Node abcd remov e b remov e c

49 © Herlihy-Shavit 200749 Removing a Node abcd remov e b remov e c

50 © Herlihy-Shavit 200750 Removing a Node abcd remov e b remov e c

51 © Herlihy-Shavit 200751 Removing a Node abcd remov e b remov e c

52 © Herlihy-Shavit 200752 Removing a Node abcd remov e b remov e c

53 © Herlihy-Shavit 200753 Removing a Node abcd remov e b remov e c

54 © Herlihy-Shavit 200754 Removing a Node abcd remov e b remov e c

55 © Herlihy-Shavit 200755 Removing a Node abcd remov e b remov e c

56 © Herlihy-Shavit 200756 Removing a Node abcd remov e b remov e c

57 © Herlihy-Shavit 200757 Uh, Oh acd remov e b remov e c Still in list

58 © Herlihy-Shavit 200758 Problem To delete node b –Swing node a’s next field to c Problem is, –Someone could delete c concurrently b acbac

59 © Herlihy-Shavit 200759 Insight If a node is locked –No one can delete node’s successor If a thread locks –Node to be deleted –And its predecessor –Then it works

60 © Herlihy-Shavit 200760 Hand-Over-Hand Again abcd remov e b

61 © Herlihy-Shavit 200761 Hand-Over-Hand Again abcd remov e b

62 © Herlihy-Shavit 200762 Hand-Over-Hand Again abcd remov e b

63 © Herlihy-Shavit 200763 Hand-Over-Hand Again abcd remov e b Found it!

64 © Herlihy-Shavit 200764 Hand-Over-Hand Again abcd remov e b Found it!

65 © Herlihy-Shavit 200765 Hand-Over-Hand Again acd remov e b Found it!

66 © Herlihy-Shavit 200766 Removing a Node abcd remov e b remov e c

67 © Herlihy-Shavit 200767 Removing a Node abcd remov e b remov e c

68 © Herlihy-Shavit 200768 Removing a Node abcd remov e b remov e c

69 © Herlihy-Shavit 200769 Removing a Node abcd remov e b remov e c

70 © Herlihy-Shavit 200770 Removing a Node abcd remov e b remov e c

71 © Herlihy-Shavit 200771 Removing a Node abcd remov e b remov e c

72 © Herlihy-Shavit 200772 Removing a Node abcd remov e b remov e c

73 © Herlihy-Shavit 200773 Removing a Node abd remov e b remov e c

74 © Herlihy-Shavit 200774 Removing a Node abd remov e b remov e c

75 © Herlihy-Shavit 200775 Removing a Node ad remov e b remov e c

76 © Herlihy-Shavit 200776 Remove method public boolean remove(Object object) { int key = object.hashCode(); Node pred, curr; try { … } finally { curr.unlock(); pred.unlock(); }}

77 © Herlihy-Shavit 200777 Remove method public boolean remove(Object object) { int key = object.hashCode(); Node pred, curr; try { … } finally { curr.unlock(); pred.unlock(); }} Key used to order node

78 © Herlihy-Shavit 200778 Remove method public boolean remove(Object object) { int key = object.hashCode(); Node pred, curr; try { … } finally { currNode.unlock(); predNode.unlock(); }} Predecessor and current nodes

79 © Herlihy-Shavit 200779 Remove method public boolean remove(Object object) { int key = object.hashCode(); Node pred, curr; try { … } finally { curr.unlock(); pred.unlock(); }} Make sure locks released

80 © Herlihy-Shavit 200780 Remove method public boolean remove(Object object) { int key = object.hashCode(); Node pred, curr; try { … } finally { curr.unlock(); pred.unlock(); }} Everything else

81 © Herlihy-Shavit 200781 Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … }

82 © Herlihy-Shavit 200782 Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } lock previous

83 © Herlihy-Shavit 200783 Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } Lock current

84 © Herlihy-Shavit 200784 Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } Traversing list

85 © Herlihy-Shavit 200785 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false;

86 © Herlihy-Shavit 200786 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Search key range

87 © Herlihy-Shavit 200787 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; At start of each loop: curr and pred locked

88 © Herlihy-Shavit 200788 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; If node found, remove it

89 © Herlihy-Shavit 200789 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; If node found, remove it

90 © Herlihy-Shavit 200790 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Unlock predecessor

91 © Herlihy-Shavit 200791 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Only one node locked!

92 © Herlihy-Shavit 200792 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; demote current

93 © Herlihy-Shavit 200793 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = currNode; curr = curr.next; curr.lock(); } return false; Find and lock new current

94 © Herlihy-Shavit 200794 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = currNode; curr = curr.next; curr.lock(); } return false; Lock invariant restored

95 © Herlihy-Shavit 200795 Remove: searching while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Otherwise, not present

96 © Herlihy-Shavit 200796 Why does this work? To remove node e –Must lock e –Must lock e’s predecessor Therefore, if you lock a node –It can’t be removed –And neither can its successor

97 © Herlihy-Shavit 200797 while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Why remove() is linearizable head ~> pred A --> curr A so the object is in the set

98 © Herlihy-Shavit 200798 while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Why remove() is linearizable Node locked, so no other thread can remove it ….

99 © Herlihy-Shavit 200799 while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Why remove() is linearizable Linearization point

100 © Herlihy-Shavit 2007100 while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Why remove() is linearizable Object not present

101 © Herlihy-Shavit 2007101 while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Why remove() is linearizable pred A --> curr A pred A.key < key key < curr A.key

102 © Herlihy-Shavit 2007102 while (curr.key <= key) { if (object == curr.object) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Why remove() is linearizable Linearization point: when curr A set to node with higher key

103 © Herlihy-Shavit 2007103 Adding Nodes To add node e –Must lock predecessor –Must lock successor Neither can be deleted –(Is successor lock actually required?)

104 © Herlihy-Shavit 2007104 Same Abstraction Map S(head) = –{ x | there exists a such that A reachable from head and a.object = x –}

105 © Herlihy-Shavit 2007105 Rep Invariant Easy to check that –Tail always reachable from head –Nodes sorted, no duplicates

106 © Herlihy-Shavit 2007106 Drawbacks Better than coarse-grained lock –Threads can traverse in parallel Still not ideal –Long chain of acquire/release –Inefficient

107 © Herlihy-Shavit 2007107 Optimistic Synchronization Find nodes without locking Lock nodes Check that everything is OK

108 © Herlihy-Shavit 2007108 Optimistic Fine Grained b d e To insert C: Optimistically traverse list to find b Lock b.curr then lock b.succ Re-Traverse list to find b and verify pred  curr Perform insert and release locks a 1 3 3 2 2 c 4

109 © Herlihy-Shavit 2007109 Same Abstraction Map S(head) = –{ x | there exists a such that A reachable from head and a.object = x –}

110 © Herlihy-Shavit 2007110 Preserving Invariants Not same as before as we traverse deleted elements But we establish properties by –Validation –After we lock target nodes

111 © Herlihy-Shavit 2007111 Scanning Deleted Items abde remov e e A Pred and curr could point to removed nodes

112 © Herlihy-Shavit 2007112 Removing a Node abcd remov e c return true

113 © Herlihy-Shavit 2007113 What Can Go Wrong? abcd remov e c remov e b

114 © Herlihy-Shavit 2007114 Check that Node is Still Accessible abcd remov e c

115 © Herlihy-Shavit 2007115 What Can Go Wrong? abcd remov e c Add b’ b’

116 © Herlihy-Shavit 2007116 What Can Go Wrong? abcd remov e c b’

117 © Herlihy-Shavit 2007117 Check that Nodes Still Adjacent abcd remov e c

118 © Herlihy-Shavit 2007118 Correctness If –Nodes b and c both locked –Node b still accessible –Node c still successor to b Then –Neither will be deleted –OK to delete and return true

119 © Herlihy-Shavit 2007119 Removing an Absent Node abde remov e c return false

120 © Herlihy-Shavit 2007120 Correctness If –Nodes b and d both locked –Node b still accessible –Node d still successor to b Then –Neither will be deleted –No thread can add c after b –OK to return false

121 © Herlihy-Shavit 2007121 Validation private boolean validate(Node pred, Node curry) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; }

122 © Herlihy-Shavit 2007122 private boolean validate(Node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; } Validation Predecessor & current nodes

123 © Herlihy-Shavit 2007123 private boolean validate(Node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; } Validation Start at the beginning

124 © Herlihy-Shavit 2007124 private boolean validate(Node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; } Validation Search range of keys

125 © Herlihy-Shavit 2007125 private boolean validate(Node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; } Validation Predecessor reachable

126 © Herlihy-Shavit 2007126 private boolean validate(Node pred, Node curry) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; } Validation Is current node next?

127 © Herlihy-Shavit 2007127 private boolean validate(Node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; } Validation Otherwise move on

128 © Herlihy-Shavit 2007128 private boolean validate(Node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; } Validation Predecessor not reachable

129 © Herlihy-Shavit 2007129 Remove: searching public boolean remove(Object object) { int key = object.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (object == curr.object) break; pred = curr; curr = curr.next; } …

130 © Herlihy-Shavit 2007130 public boolean remove(Object object) { int key = object.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (object == curr.object) break; pred = curr; curr = curr.next; } … Remove: searching Search key

131 © Herlihy-Shavit 2007131 public boolean remove(Object object) { int key = object.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (object == curr.object) break; pred = curr; curr = curr.next; } … Remove: searching Retry on synchronization conflict

132 © Herlihy-Shavit 2007132 public boolean remove(Object object) { int key = object.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (object == curr.object) break; pred = curr; curr = curr.next; } … Remove: searching Examine predecessor and current nodes

133 © Herlihy-Shavit 2007133 public boolean remove(Object object) { int key = object.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (object == curr.object) break; pred = curr; curr = curr.next; } … Remove: searching Search by key

134 © Herlihy-Shavit 2007134 public boolean remove(Object object) { int key = object.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (object == curr.object) break; pred = curr; curr = curr.next; } … Remove: searching Stop if we find object

135 © Herlihy-Shavit 2007135 public boolean remove(Object object) { int key = object.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (object == curr.object) break; pred = curr; curr = curr.next; } … Remove: searching Move along

136 © Herlihy-Shavit 2007136 On Exit from Loop If object is present –curr holds object –pred just before curr If object is absent –curr has first higher key –pred just before curr Assuming no synchronization problems

137 © Herlihy-Shavit 2007137 Remove Method try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.object == object) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}}

138 © Herlihy-Shavit 2007138 try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.object == object) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Remove Method Always unlock

139 © Herlihy-Shavit 2007139 try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.object == object) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Remove Method Lock both nodes

140 © Herlihy-Shavit 2007140 try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.object == object) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Remove Method Check for synchronization conflicts

141 © Herlihy-Shavit 2007141 try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.object == object) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Remove Method Object found, remove node

142 © Herlihy-Shavit 2007142 try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.object == object) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Remove Method Object not found

143 © Herlihy-Shavit 2007143 Summary: Wait-free Traversals b c d a 1.Traverse removed nodes 2.Must have non-interference 3.Natural in GC languages like Java™ Reachable = in the set

144 © Herlihy-Shavit 2007144 Summary Optimistic List b c e a 1.Limited Hotspots (Only at locked Add(), Remove(), Find() destination locations, not traversals) 2.But two traversals 3.Yet traversals are wait-free!

145 © Herlihy-Shavit 2007145 So Far, So Good Much less lock acquisition/release –Performance –Concurrency Problems –Need to traverse list twice –contains() method acquires locks Most common method call

146 © Herlihy-Shavit 2007146 Evaluation Optimistic works if –cost of scanning twice without locks < –cost of Scanning once with locks Drawback –Contains() acquires locks –90% of calls in many apps

147 © Herlihy-Shavit 2007147 Lazy List Like optimistic, except –Scan once –Contains() never locks … Key insight –Removing nodes causes trouble –Do it “lazily”

148 © Herlihy-Shavit 2007148 Lazy List Remove Method –Scans list (as before) –Locks predecessor & current (as before) Logical delete –Marks current node as removed (new!) Physical delete –Redirects predecessor’s next (as before)

149 © Herlihy-Shavit 2007149 Lazy Removal a 0 0 0 a b c 0 d 1 c Logical Removal = Set Mark Bit Physical Removal Add a separate mark bit field to each node: 1. mark node as deleted 2. redirect pointer

150 © Herlihy-Shavit 2007150 Lazy Removal a 0 0 0 a b c 0 d 1 3 2 2 1 c 4 Logical Removal Physical Removal Release Locks 1.Optimistically traverse list to find b 2.Lock b.curr then lock b.succ 3.verify pred  curr 4.Perform logical then physical removal 5.Release locks

151 © Herlihy-Shavit 2007151 Lazy List All Methods –Scan through locked and marked nodes –Removing a node doesn’t slow down other method calls … Must still lock pred and curr nodes.

152 © Herlihy-Shavit 2007152 Validation No need to rescan list! Check that pred is not marked Check that curr is not marked Check that pred points to curr

153 © Herlihy-Shavit 2007153 Business as Usual abcd

154 © Herlihy-Shavit 2007154 Business as Usual abcd

155 © Herlihy-Shavit 2007155 Business as Usual abcd

156 © Herlihy-Shavit 2007156 Business as Usual abcd

157 © Herlihy-Shavit 2007157 Business as Usual abcd

158 © Herlihy-Shavit 2007158 Interference abcd

159 © Herlihy-Shavit 2007159 Interference abcd

160 © Herlihy-Shavit 2007160 Interference abcd

161 © Herlihy-Shavit 2007161 Interference abcd Remove c

162 © Herlihy-Shavit 2007162 Validation abcd b not marked

163 © Herlihy-Shavit 2007163 Interference abcd c not marked

164 © Herlihy-Shavit 2007164 Interference abcd b still points to c

165 © Herlihy-Shavit 2007165 Logical Delete abcd mark

166 © Herlihy-Shavit 2007166 Scan Through abcd So what? Remove c

167 © Herlihy-Shavit 2007167 Physical Deletion abcd So what? Remove c

168 © Herlihy-Shavit 2007168 New Abstraction Map S(head) = –{ x | there exists node a such that A reachable from head and a.object = x and a is unmarked –}

169 © Herlihy-Shavit 2007169 Invariant If not marked then item in the set and reachable from head and if not yet traversed it is reachable from pred

170 © Herlihy-Shavit 2007170 Validation private boolean validate(Node pred, Node curr) { return !pred.next.marked && !curr.next.marked && pred.next == curr); }

171 © Herlihy-Shavit 2007171 private boolean validate(Node pred, Node curr) { return !pred.marked && !curr.marked && pred.next == curr); } List Validate Method Predecessor not Logically removed

172 © Herlihy-Shavit 2007172 private boolean validate(Node pred, Node curr) { return !pred.next.marked && !curr.next.marked && pred.next == curr); } List Validate Method Current not Logically removed

173 © Herlihy-Shavit 2007173 private boolean validate(Node pred, Node curr) { return !pred.next.marked && !curr.next.marked && pred.next == curr); } List Validate Method Predecessor still Points to current

174 © Herlihy-Shavit 2007174 Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}}

175 © Herlihy-Shavit 2007175 Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Validate as before

176 © Herlihy-Shavit 2007176 Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Key found

177 © Herlihy-Shavit 2007177 Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Logical remove

178 © Herlihy-Shavit 2007178 Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} physical remove

179 © Herlihy-Shavit 2007179 Contains public boolean contains(Object object) { int key = object.hashCode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; } return curr.key == key && !curr.marked; }

180 © Herlihy-Shavit 2007180 Contains public boolean contains(Object object) { int key = object.hashCode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; } return curr.key == key && !curr.marked; } Start at the head

181 © Herlihy-Shavit 2007181 Contains public boolean contains(Object object) { int key = object.hashCode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; } return curr.key == key && !curr.marked; } Search key range

182 © Herlihy-Shavit 2007182 Contains public boolean contains(Object object) { int key = object.hashCode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; } return curr.key == key && !curr.marked; } Traverse without locking (nodes may have been removed)

183 © Herlihy-Shavit 2007183 Contains public boolean contains(Object object) { int key = object.hashCode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; } return curr.key == key && !curr.marked; } Present and undeleted?

184 © Herlihy-Shavit 2007184 Summary: Wait-free Contains a 0 0 0 a b c 0 e 1 d Use Mark bit + Fact that List is ordered 1.Not marked  in the set 2.Marked or missing  not in the set

185 © Herlihy-Shavit 2007185 Lazy List a 0 0 0 a b c 0 e 1 d Lazy Add() and Remove() + Wait-free Contains()

186 © Herlihy-Shavit 2007186 Evaluation Good: –Contains method doesn’t need to lock –In fact, its wait-free! –Good because typically high % contains() –Uncontended calls don’t re-traverse Bad –Contended calls do re-traverse –Traffic jam if one thread delays

187 © Herlihy-Shavit 2007187 Traffic Jam Any concurrent data structure based on mutual exclusion has a weakness If one thread –Enters critical section –And “eats the big muffin” (stops running) Cache miss, page fault, descheduled … –Everyone else using that lock is stuck!

188 © Herlihy-Shavit 2007188 Reminder: Lock-Free Data Structures No matter what … –Some thread will complete method call –Even if others halt at malicious times –Weaker than wait-free, yet Implies that –You can’t use locks –Um, that’s why they call it lock-free

189 © Herlihy-Shavit 2007189 Lock-free Lists Next logical step Eliminate locking entirely Contains() wait-free and Add() and Remove() lock-free Use only compareAndSet() What could go wrong?

190 © Herlihy-Shavit 2007190 Adding a Node abc

191 © Herlihy-Shavit 2007191 Adding a Node abcb

192 © Herlihy-Shavit 2007192 Adding a Node abcb CAS

193 © Herlihy-Shavit 2007193 Adding a Node abcb

194 © Herlihy-Shavit 2007194 Adding a Node abcb

195 © Herlihy-Shavit 2007195 Removing a Node abcd remov e b remov e c CAS

196 © Herlihy-Shavit 2007196 Look Familiar? abcd remov e b remov e c Still in list

197 © Herlihy-Shavit 2007197 Problem Method updates node’s next field After node has been removed

198 © Herlihy-Shavit 2007198 Solution Use AtomicMarkableReference Atomically –Swing reference and –Update flag Remove in two steps –Set mark bit in next field –Redirect predecessor’s pointer

199 © Herlihy-Shavit 2007199 Marking a Node AtomicMarkableReference class –Java.util.concurrent.atomic package address F mark bit Reference

200 © Herlihy-Shavit 2007200 Extracting Reference & Mark Public T get(boolean[] marked);

201 © Herlihy-Shavit 2007201 Extracting Reference & Mark Public T get(boolean[] marked); Returns reference to object of type T Returns mark at array index 0!

202 © Herlihy-Shavit 2007202 Changing State Public boolean compareAndSet( T expectedRef, T updateRef, boolean expectedMark, boolean updateMark);

203 © Herlihy-Shavit 2007203 Changing State Public boolean compareAndSet( T expectedRef, T updateRef, boolean expectedMark, boolean updateMark); If this is the current reference … And this is the current mark …

204 © Herlihy-Shavit 2007204 Changing State Public boolean compareAndSet( T expectedRef, T updateRef, boolean expectedMark, boolean updateMark); …then change to this new reference … … and this new mark

205 © Herlihy-Shavit 2007205 Changing State public boolean attemptMark( T expectedRef, boolean updateMark);

206 © Herlihy-Shavit 2007206 Changing State public boolean attemptMark( T expectedRef, boolean updateMark); If this is the current reference …

207 © Herlihy-Shavit 2007207 Changing State public boolean attemptMark( T expectedRef, boolean updateMark);.. then change to this new mark.

208 © Herlihy-Shavit 2007208 Removing a Node abcd remov e c CAS

209 © Herlihy-Shavit 2007209 Removing a Node abd remov e b remov e c c CAS failed

210 © Herlihy-Shavit 2007210 Removing a Node abd remov e b remov e c c

211 © Herlihy-Shavit 2007211 Removing a Node ad remov e b remov e c

212 © Herlihy-Shavit 2007212 Traversing the List Q: what do you do when you find a “logically” deleted node in your path? A: finish the job. –CAS the predecessor’s next field –Proceed (repeat as needed)

213 © Herlihy-Shavit 2007213 Lock-Free Traversal abcd CAS Uh-oh

214 © Herlihy-Shavit 2007214 The Window Class class Window { public Node pred; public Node curr; Window(Node pred, Node curr) { this.pred = pred; this.curr = curr; }

215 © Herlihy-Shavit 2007215 The Window Class class Window { public Node pred; public Node curr; Window(Node pred, Node curr) { this.pred = pred; this.curr = curr; } A container for pred and current values

216 © Herlihy-Shavit 2007216 Using the Find Method Window window = find(head, key); Node pred = window.pred; curr = window.curr;

217 © Herlihy-Shavit 2007217 Using the Find Method Window window = find(head, key); Node pred = window.pred; curr = window.curr; Find returns window

218 © Herlihy-Shavit 2007218 Using the Find Method Window window = find(head, key); Node pred = window.pred; curr = window.curr; Extract pred and curr

219 © Herlihy-Shavit 2007219 The Find Method Window window = find(head, key); At some instant, predcurrsucc object or …

220 © Herlihy-Shavit 2007220 The Find Method Window window = find(head,key); At some instant, pred curr= succ succ object not in list

221 © Herlihy-Shavit 2007221 Remove public boolean remove(T object) { Boolean snip; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key != key) { return false; } else { Node succ = curr.next.getReference(); snip = curr.next.attemptMark(succ, true); if (!snip) continue; pred.next.compareAndSet(curr, succ, false, false); return true; }}}

222 © Herlihy-Shavit 2007222 Remove public boolean remove(T object) { Boolean snip; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key != key) { return false; } else { Node succ = curr.next.getReference(); snip = curr.next.attemptMark(succ, true); if (!snip) continue; pred.next.compareAndSet(curr, succ, false, false); return true; }}} Keep trying

223 © Herlihy-Shavit 2007223 Remove public boolean remove(T object) { Boolean snip; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key != key) { return false; } else { Node succ = curr.next.getReference(); snip = curr.next.attemptMark(succ, true); if (!snip) continue; pred.next.compareAndSet(curr, succ, false, false); return true; }}} Find neighbors

224 © Herlihy-Shavit 2007224 Remove public boolean remove(T object) { Boolean snip; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key != key) { return false; } else { Node succ = curr.next.getReference(); snip = curr.next.attemptMark(succ, true); if (!snip) continue; pred.next.compareAndSet(curr, succ, false, false); return true; }}} She’s not there …

225 © Herlihy-Shavit 2007225 Remove public boolean remove(T object) { Boolean snip; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key != key) { return false; } else { Node succ = curr.next.getReference(); snip = curr.next.attemptMark(succ, true); if (!snip) continue; pred.next.compareAndSet(curr, succ, false, false); return true; }}} Try to mark node as deleted

226 © Herlihy-Shavit 2007226 Remove public boolean remove(T object) { Boolean snip; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key != key) { return false; } else { Node succ = curr.next.getReference(); snip = curr.next.attemptMark(succ, true); if (!snip) continue; pred.next.compareAndSet(curr, succ, false, false); return true; }}} If it doesn’t work, just retry, if it does, job essentially done

227 © Herlihy-Shavit 2007227 Remove public boolean remove(T object) { Boolean snip; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key != key) { return false; } else { Node succ = curr.next.getReference(); snip = curr.next.attemptMark(succ, true); if (!snip) continue; pred.next.compareAndSet(curr, succ, false, false); return true; }}} Try to advance reference (if we don’t succeed, someone else did or will). a

228 © Herlihy-Shavit 2007228 Add public boolean add(T object) { boolean splice; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key == key) { return false; } else { Node node = new Node(object); node.next = new AtomicMarkableRef(curr, false); if (pred.next.compareAndSet(curr, node, false, false)) {return true; }}}}

229 © Herlihy-Shavit 2007229 Add public boolean add(T object) { boolean splice; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key == key) { return false; } else { Node node = new Node(object); node.next = new AtomicMarkableRef(curr, false); if (pred.next.compareAndSet(curr, node, false, false)) {return true; }}}} Object already there.

230 © Herlihy-Shavit 2007230 Add public boolean add(T object) { boolean splice; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key == key) { return false; } else { Node node = new Node(object); node.next = new AtomicMarkableRef(curr, false); if (pred.next.compareAndSet(curr, node, false, false)) {return true; }}}} create new node

231 © Herlihy-Shavit 2007231 Add public boolean add(T object) { boolean splice; while (true) { Window window = find(head, key); Node pred = window.pred, curr = window.curr; if (curr.key == key) { return false; } else { Node node = new Node(object); node.next = new AtomicMarkableRef(curr, false); if (pred.next.compareAndSet(curr, node, false, false)) {return true }}}} Install new node, else retry loop

232 © Herlihy-Shavit 2007232 Wait-free Contains public boolean contains(T object) { boolean marked; int key = object.hashCode(); Node curr = this.head; while (curr.key < key) curr = curr.next; Node succ = curr.next.get(marked); return (curr.key == key && !marked[0]) }

233 © Herlihy-Shavit 2007233 Wait-free Contains public boolean contains(T object) { boolean marked; int key = object.hashCode(); Node curr = this.head; while (curr.key < key) curr = curr.next; Node succ = curr.next.get(marked); return (curr.key == key && !marked[0]) } Only diff from lazy list is that we get and check marked

234 © Herlihy-Shavit 2007234 Lock-free Find public Window find(Node head, int key) { Node pred = null, curr = null, succ = null; boolean[] marked = {false}; boolean snip; retry: while (true) { pred = head; curr = pred.next.getReference(); while (true) { succ = curr.next.get(marked); while (marked[0]) { … } if (curr.key >= key) return new Window(pred, curr); pred = curr; curr = succ; } }}

235 © Herlihy-Shavit 2007235 Lock-free Find public Window find(Node head, int key) { Node pred = null, curr = null, succ = null; boolean[] marked = {false}; boolean snip; retry: while (true) { pred = head; curr = pred.next.getReference(); while (true) { succ = curr.next.get(marked); while (marked[0]) { … } if (curr.key >= key) return new Window(pred, curr); pred = curr; curr = succ; } }} If list changes while traversed, start over Lock-Free because we start over only if someone else makes progress

236 © Herlihy-Shavit 2007236 public Window find(Node head, int key) { Node pred = null, curr = null, succ = null; boolean[] marked = {false}; boolean snip; retry: while (true) { pred = head; curr = pred.next.getReference(); while (true) { succ = curr.next.get(marked); while (marked[0]) { … } if (curr.key >= key) return new Window(pred, curr); pred = curr; curr = succ; } }} Lock-free Find Start looking from head

237 © Herlihy-Shavit 2007237 public Window find(Node head, int key) { Node pred = null, curr = null, succ = null; boolean[] marked = {false}; boolean snip; retry: while (true) { pred = head; curr = pred.next.getReference(); while (true) { succ = curr.next.get(marked); while (marked[0]) { … } if (curr.key >= key) return new Window(pred, curr); pred = curr; curr = succ; } }} Lock-free Find Move down the list

238 © Herlihy-Shavit 2007238 public Window find(Node head, int key) { Node pred = null, curr = null, succ = null; boolean[] marked = {false}; boolean snip; retry: while (true) { pred = head; curr = pred.next.getReference(); while (true) { succ = curr.next.get(marked); while (marked[0]) { … } if (curr.key >= key) return new Window(pred, curr); pred = curr; curr = succ; } }} Lock-free Find Get ref to successor and current deleted bit

239 © Herlihy-Shavit 2007239 public Window find(Node head, int key) { Node pred = null, curr = null, succ = null; boolean[] marked = {false}; boolean snip; retry: while (true) { pred = head; curr = pred.next.getReference(); while (true) { succ = curr.next.get(marked); while (marked[0]) { … } if (curr.key >= key) return new Window(pred, curr); pred = curr; curr = succ; } }} Lock-free Find Try to remove deleted nodes in path…code details soon

240 © Herlihy-Shavit 2007240 public Window find(Node head, int key) { Node pred = null, curr = null, succ = null; boolean[] marked = {false}; boolean snip; retry: while (true) { pred = head; curr = pred.next.getReference(); while (true) { succ = curr.next.get(marked); while (marked[0]) { … } if (curr.key >= key) return new Window(pred, curr); pred = curr; curr = succ; } }} Lock-free Find If found curr key that is greater or equal, return pred and curr

241 © Herlihy-Shavit 2007241 public Window find(Node head, int key) { Node pred = null, curr = null, succ = null; boolean[] marked = {false}; boolean snip; retry: while (true) { pred = head; curr = pred.next.getReference(); while (true) { succ = curr.next.get(marked); while (marked[0]) { … } if (curr.key >= key) return new Window(pred, curr); pred = curr; curr = succ; } }} Lock-free Find Otherwise advance “window” and loop again

242 © Herlihy-Shavit 2007242 Lock-free Find retry: while (true) { … while (marked[0]) { snip = pred.next.compareAndSet(curr, succ, false, false); if (!snip) continue retry; curr = succ; succ = curr.next.get(marked); } …

243 © Herlihy-Shavit 2007243 Lock-free Find retry: while (true) { … while (marked[0]) { snip = pred.next.compareAndSet(curr, succ, false, false); if (!snip) continue retry; curr = succ; succ = curr.next.get(marked); } … Try to snip out node

244 © Herlihy-Shavit 2007244 Lock-free Find retry: while (true) { … while (marked[0]) { snip = pred.next.compareAndSet(curr, succ, false, false); if (!snip) continue retry; curr = succ; succ = curr.next.get(marked); } … if predecessor’s next field changed must retry whole traversal

245 © Herlihy-Shavit 2007245 Lock-free Find retry: while (true) { … while (marked[0]) { snip = pred.next.compareAndSet(curr, succ, false, false); if (!snip) continue retry; curr = succ; succ = curr.next.get(marked); } … Otherwise move on to check if next node deleted

246 © Herlihy-Shavit 2007246 Summary: Summary: Lock- free Removal a 0 0 0 a b c 0 e 1 c Logical Removal = Set Mark Bit Physical Removal CAS pointer Use CAS to verify pointer is correct Not enough!

247 © Herlihy-Shavit 2007247 Summary: Summary: Lock-free Removal a 0 0 0 a b c 0 e 1 c Logical Removal = Set Mark Bit Physical Removal CAS 0 d Problem: d not added to list… Must Prevent manipulation of removed node’s pointer Node added Before Physical Removal CAS

248 © Herlihy-Shavit 2007248 Our Solution: Combine Bit and Pointer a 0 0 0 a b c 0 e 1 c Logical Removal = Set Mark Bit Physical Removal CAS 0 d Mark-Bit and Pointer are CASed together Fail CAS: Node not added after logical Removal

249 © Herlihy-Shavit 2007249 A Lock-free Algorithm a 0 0 0 a b c 0 e 1 c 1. Add() and Remove() physically clean up (remove) marked nodes 2. Wait-free find() traverses both marked and removed nodes

250 © Herlihy-Shavit 2007250 Performance On 16 node shared memory machine Benchmark throughput of Java List-based Set algs. Vary % of Contains() method Calls.

251 © Herlihy-Shavit 2007251 High Contains Ratio Lock-free Lazy list Course Grained Fine Lock-coupling

252 © Herlihy-Shavit 2007252 Low Contains Ratio

253 © Herlihy-Shavit 2007253 As Contains Ratio Increases % Contains()

254 © Herlihy-Shavit 2007254 Summary Coarse-grained locking Fine-grained locking Optimistic synchronization Lock-free synchronization

255 © Herlihy-Shavit 2007255 “To Lock or Not to Lock” Locking vs. Non-blocking: Extremist views on both sides The answer: nobler to compromise, combine locking and non-blocking –Example: Lazy list combines blocking add() and remove() and a wait-free contains() –Blocking/non-blocking is a property of a method


Download ppt "Linked Lists: Locking, Lock-Free, and Beyond … The Art of Multiprocessor Programming Spring 2007."

Similar presentations


Ads by Google