Presentation is loading. Please wait.

Presentation is loading. Please wait.

CO890 CONCURRENCY & PARALLELISM

Similar presentations


Presentation on theme: "CO890 CONCURRENCY & PARALLELISM"— Presentation transcript:

1 CO890 CONCURRENCY & PARALLELISM
FINE GRAINED SYNCHRONIZATION 2017

2 Concurrent Objects Adding threads should not lower throughput
Contention effects Mostly fixed by Queue locks Should increase throughput Not possible if inherently sequential Surprising things are parallelizable Art of Multiprocessor Programming

3 64-core 4-node AMD; HotSpot 1.7
CO890 Concurrency and Parallelism, Richard Jones, University of Kent

4 Azul Vega 3, 864 processors; Azul VM 1.6
CO890 Concurrency and Parallelism, Richard Jones, University of Kent

5 Azul Vega 3, 864 processors; Azul VM 1.6
CO890 Concurrency and Parallelism, Richard Jones, University of Kent

6 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 Sequential bottleneck Adding more threads does not improve throughput Struggle to keep it from getting worse Art of Multiprocessor Programming

7 1. 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 Art of Multiprocessor Programming

8 2. Optimistic Synchronization
Search without locking … If you find it, lock and check … OK: we are done Oops: start over Evaluation Usually cheaper than locking Mistakes are expensive Art of Multiprocessor Programming

9 3. Lazy Synchronization Postpone hard work
Removing components is tricky Logical removal Mark component to be deleted Physical removal Do what needs to be done Art of Multiprocessor Programming

10 4. Lock-Free Synchronization
Don’t use locks at all Use compareAndSet() & relatives … Advantages No scheduler assumptions/support Disadvantages Complex Sometimes high overhead Art of Multiprocessor Programming

11 Progress guarantees A method is
Wait-free: if it guarantees that every call always finishes in a finite number of steps regardless of the actions of other threads Lock-free: guarantees that some call always finishes in a finite number of steps Obstruction-free: if, given a long enough period of isolated execution, any call will finish in a finite number of steps strongest weakest CO890 Concurrency and Parallelism, Richard Jones, University of Kent

12 Example: Linked List List-based Set public boolean add(T x);
public boolean remove(T x); public boolean contains(T x);

13 The List-based Set Sorted with Sentinel nodes
-∞ a b c +∞ Sorted with Sentinel nodes (min & max possible keys) Art of Multiprocessor Programming

14 Reasoning about Concurrent Objects
Invariant: Property that always holds Established because True when object is created Truth preserved by each method Each step of each method Art of Multiprocessor Programming

15 Specifically … Invariants preserved by add(), remove(), contains()
Most steps are trivial Usually one step tricky Often a linearization point Linearizability: each method call should appear to take effect instantaneously Linearization point: where the method takes effect With locks: the critical section Lock-free: a single step where effect becomes visible to other method calls Invariants make sense only if methods considered are the only modifiers. Language encapsulation helps: List nodes not visible outside class. Freedom from interference needed even for removed nodes since some algorithms traverse removed nodes. Careful with malloc() & free()! Garbage-collection helps here Art of Multiprocessor Programming

16 Coarse Grained Locking
b d c Art of Multiprocessor Programming

17 Coarse Grained Locking
b d honk! honk! c Simple but hotspot + bottleneck Art of Multiprocessor Programming

18 Coarse-Grained Locking
Easy, same as synchronized methods “One lock to rule them all …” Simple, clearly correct Works poorly with contention Art of Multiprocessor Programming

19 Fine-grained Locking Requires careful thought Split object into pieces
“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 We can improve concurrency by locking individual entries, rather than locking the list as a whole. Instead of placing a lock on the entire list, let us add a lock to each entry, along with lock() and unlock() methods. As a thread traverses the list, it locks each entry when it first visits, and sometime later releases it. Suchfine-grained locking permits concurrent threads to traverse the list together in a pipelined fashion. Art of Multiprocessor Programming

20 Hand-over-hand locking
b c Art of Multiprocessor Programming

21 Hand-over-hand locking
b c Art of Multiprocessor Programming

22 Hand-over-hand locking
b c Art of Multiprocessor Programming

23 Removing a Node a b c d remove(b) Art of Multiprocessor Programming

24 Removing a Node a b c d remove(b) Art of Multiprocessor Programming

25 Removing a Node a b c d remove(b) Art of Multiprocessor Programming

26 Removing a Node a b c d remove(b)
Here is a naive approach to removing a node. Each thread locks the node being examined and its predecessor. Art of Multiprocessor Programming

27 Removing a Node Why do we need to always hold 2 locks? a c d remove(b)
Here is a naive approach to removing a node. Each thread locks the node being examined and its predecessor. Art of Multiprocessor Programming

28 Concurrent Removes a b c d remove(c) remove(b)
Supposed we only locked one node and not two. Consider two concurrent threads that want to remove adjacent nodes. The red thread looks for the predecessor to c, while the green thread looks for the predecessor to b. Art of Multiprocessor Programming

29 Oops! c not removed! a c d remove(c) remove(b)
But the final effect is to remove b but not c! Art of Multiprocessor Programming

30 Removing a Node a b c d remove(c) Must acquire Lock of b
Art of Multiprocessor Programming

31 Removing a Node a b c d remove(c) Wait!
Art of Multiprocessor Programming

32 Removing a Node a b d Proceed to remove(b)
Art of Multiprocessor Programming

33 Make sure locks released
public boolean remove(Item item) { int key = item.key(); Node pred, curr; try { } finally { curr.unlock(); pred.unlock(); } Make sure locks released Remove method Art of Multiprocessor Programming

34 Remove method try { pred = this.head; pred.lock(); curr = pred.next;
curr.lock(); } finally { … } Remove method Art of Multiprocessor Programming

35 Remove: searching while (curr.key <= key) {
if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Art of Multiprocessor Programming

36 Remove: searching Search key range while (curr.key <= key) {
if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Search key range Art of Multiprocessor Programming

37 At start of each loop: curr and pred locked
Remove: searching while (curr.key <= key) { if (item == curr.item) { 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 Art of Multiprocessor Programming

38 If item found, remove node
Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; If item found, remove node Art of Multiprocessor Programming

39 Remove: searching If node found, remove it
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; If node found, remove it Art of Multiprocessor Programming

40 Remove: searching Unlock predecessor while (curr.key <= key) {
if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Art of Multiprocessor Programming

41 Remove: searching Only one node locked! while (curr.key <= key) {
if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Art of Multiprocessor Programming

42 Remove: searching demote current while (curr.key <= key) {
if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; demote current Art of Multiprocessor Programming

43 Find and lock new current
Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = currNode; curr = curr.next; curr.lock(); return false; Find and lock new current Art of Multiprocessor Programming

44 Lock invariant restored
Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = currNode; curr = curr.next; curr.lock(); return false; Lock invariant restored Art of Multiprocessor Programming

45 Remove: searching Otherwise, not present while (curr.key <= key) {
if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Otherwise, not present Art of Multiprocessor Programming

46 Why remove() is linearizable
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; pred reachable from head curr is pred.next So curr.item is in the set Art of Multiprocessor Programming

47 Why remove() is linearizable
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Linearization point if item is present Art of Multiprocessor Programming

48 Why remove() is linearizable
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Node locked, so no other thread can remove it …. Art of Multiprocessor Programming

49 Why remove() is linearizable
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Item not present Art of Multiprocessor Programming

50 Why remove() is linearizable
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; pred reachable from head curr is pred.next pred.key < key key < curr.key Art of Multiprocessor Programming

51 Why remove() is linearizable
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Linearization point Notice that we could also have the linearization points be those when the locks are acquired. Art of Multiprocessor Programming

52 Adding Nodes To add node e Neither can be deleted
Must lock predecessor Must lock successor Neither can be deleted Is successor lock actually required? successor lock not actually required! Art of Multiprocessor Programming

53 Drawbacks Better than coarse-grained lock Still not ideal
Threads can traverse in parallel Still not ideal Long chain of acquire/release Inefficient Art of Multiprocessor Programming

54 Optimistic Synchronization
Find nodes without locking Lock nodes Check that everything is OK Art of Multiprocessor Programming

55 Optimistic: traverse without locking
b d e Aha! add(c) Art of Multiprocessor Programming

56 Optimistic: Lock and Load
b d e add(c) Art of Multiprocessor Programming

57 What could go wrong? a b d e remove(b) Aha! add(c)
Art of Multiprocessor Programming

58 Validate – Part 1 (while holding locks)
b d e Yes, b still reachable from head add(c) Need (1) validation and (2) absence of interference. GC helps (2) as it prevents an node being recycled while it is being traversed. Not starvation-free. Art of Multiprocessor Programming

59 What Else Can Go Wrong? a b d e add(c)
Art of Multiprocessor Programming

60 What Else Can Go Wrong? b’ a b d e add(b’) add(c)
Art of Multiprocessor Programming

61 What Else Can Go Wrong? b’ a b d e Aha! add(c)
Ooops! Although b is still reachable it no longer points to d. Art of Multiprocessor Programming

62 Optimistic: Linearization Point
b d e c add(c) Art of Multiprocessor Programming

63 Invariants Careful: we may traverse deleted nodes
But we establish properties by Validation After we lock target nodes If Nodes b and c both locked and Node b still accessible and Node c still successor to b Then neither will be deleted OK to delete and return true Art of Multiprocessor Programming

64 Recap Wait-free and lock-free methods
Reasoning with invariants; linearisation points Coarse grain locking Simple, clearly correct. Sequential bottleneck Fine-grain synchronisation Independently synchronised objects Hand over hand locking; long chain of acquire/release Optimistic synchronisation Find node without locking; lock and validate CO890 Concurrency and Parallelism, Richard Jones, University of Kent

65 Validation 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; Art of Multiprocessor Programming

66 Predecessor & current nodes
Validation 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; Predecessor & current nodes Art of Multiprocessor Programming

67 Validation Begin at the beginning 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; Begin at the beginning Art of Multiprocessor Programming

68 Validation Search range of keys 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; Search range of keys Art of Multiprocessor Programming

69 Predecessor reachable
Validation 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; Predecessor reachable Art of Multiprocessor Programming

70 Validation Is current node next? 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; Is current node next? Art of Multiprocessor Programming

71 Validation Otherwise move on 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; Otherwise move on Art of Multiprocessor Programming

72 Predecessor not reachable
Validation Predecessor not reachable 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; Art of Multiprocessor Programming

73 Remove: searching public boolean remove(Item item) {
int key = item.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; } … Art of Multiprocessor Programming

74 Remove: searching Search key public boolean remove(Item item) {
int key = item.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; } … Search key Art of Multiprocessor Programming

75 Retry on synchronization conflict
Remove: searching public boolean remove(Item item) { int key = item.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; } … Retry on synchronization conflict Art of Multiprocessor Programming

76 Examine predecessor and current nodes
Remove: searching public boolean remove(Item item) { int key = item.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; } … Examine predecessor and current nodes Art of Multiprocessor Programming

77 Remove: searching Search by key public boolean remove(Item item) {
int key = item.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; } … Search by key Art of Multiprocessor Programming

78 Remove: searching Stop if we find item
public boolean remove(Item item) { int key = item.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; } … Stop if we find item Art of Multiprocessor Programming

79 Remove: searching Move along public boolean remove(Item item) {
int key = item.hashCode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; } … Move along Art of Multiprocessor Programming

80 On Exit from Loop If item is present If item is absent
curr holds item pred just before curr If item is absent curr has first higher key Assuming no synchronization problems Art of Multiprocessor Programming

81 Remove Method try { pred.lock(); curr.lock();
if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Art of Multiprocessor Programming

82 Remove Method Always unlock try { pred.lock(); curr.lock();
if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Always unlock Art of Multiprocessor Programming

83 Remove Method Lock both nodes try { pred.lock(); curr.lock();
if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Lock both nodes Art of Multiprocessor Programming

84 Check for synchronization conflicts
Remove Method try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} Check for synchronization conflicts Art of Multiprocessor Programming

85 target found, remove node
Remove Method try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} target found, remove node Art of Multiprocessor Programming

86 Remove Method target not found try { pred.lock(); curr.lock();
if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; } else { return false; }}} finally { pred.unlock(); curr.unlock(); }}} target not found Art of Multiprocessor Programming

87 Optimistic List Limited hot-spots Traversals are wait-free Problems
Targets of add(), remove(), contains() No contention on traversals Much less lock acquisition/release Performance Concurrency Traversals are wait-free Problems Need to traverse list twice contains() method acquires locks Art of Multiprocessor Programming

88 Evaluation Optimistic is effective if Drawback
cost of scanning twice without locks is less than the cost of scanning once with locks Drawback contains() acquires locks 90% of calls in many applications Art of Multiprocessor Programming

89 Lazy List Like optimistic, except Key insight Scan once
contains(x) never locks … Key insight Removing nodes causes trouble Do it “lazily” Art of Multiprocessor Programming

90 Lazy List remove() Logical delete Physical delete
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) Art of Multiprocessor Programming

91 Lazy Removal a a b c d Present in list
Art of Multiprocessor Programming

92 Lazy Removal a a b c d Logically deleted
Art of Multiprocessor Programming

93 Lazy Removal a a b c d Physically deleted
Art of Multiprocessor Programming

94 Lazy List All Methods Must still lock pred and curr nodes.
Scan through locked and marked nodes Removing a node doesn’t slow down other method calls … Must still lock pred and curr nodes. Art of Multiprocessor Programming

95 Validation No need to rescan list! Check that pred is not marked
Check that curr is not marked Check that pred points to curr Art of Multiprocessor Programming

96 Business as Usual a b c Art of Multiprocessor Programming

97 Business as Usual a b c remove(b) Art of Multiprocessor Programming

98 Business as Usual a b c a not marked Art of Multiprocessor Programming

99 Business as Usual a b c a still points to b
Art of Multiprocessor Programming

100 Business as Usual a b c Logical delete
Art of Multiprocessor Programming

101 Business as Usual a b c physical delete
Art of Multiprocessor Programming

102 Business as Usual a b c Art of Multiprocessor Programming

103 Invariant If not marked then item is in the set
and reachable from head and if not yet traversed it is reachable from pred Art of Multiprocessor Programming

104 Validation private boolean validate(Node pred, Node curr) { return
!pred.marked && !curr.marked && pred.next == curr); } Art of Multiprocessor Programming

105 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(); }}} Art of Multiprocessor Programming

106 Remove Validate as before 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 Art of Multiprocessor Programming

107 Remove Key found 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 Art of Multiprocessor Programming

108 Remove Logical 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 Art of Multiprocessor Programming

109 Remove physical 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 Art of Multiprocessor Programming

110 Traverse without locking (nodes may have been removed)
Contains public boolean contains(Item item) { int key = item.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) Art of Multiprocessor Programming

111 Evaluation Good: Bad contains() doesn’t lock In fact, its wait-free!
Good because typically high %age of contains() Uncontended calls don’t re-traverse Bad Contended add() and remove() calls do re-traverse Traffic jam if one thread delays Art of Multiprocessor Programming

112 Under the following conditions:
          This work is licensed under a Creative Commons Attribution-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 "CO890 CONCURRENCY & PARALLELISM"

Similar presentations


Ads by Google