Linked Lists: Optimistic, Lock-Free, … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit Modified by Pavol Černý,
Post on 05-Jan-2016
217 Views
Preview:
Transcript
Linked Lists: Optimistic, Lock-Free, …
Companion slides forThe Art of Multiprocessor Programming
by Maurice Herlihy & Nir Shavit
Modified by Pavol Černý, Programming Paradigms for
Concurrency, Fall 2010, IST Austria
Art of Multiprocessor Programming 2
Set Interface
• Unordered collection of items• No duplicates• Methods
– add(x) put x in set– remove(x) take x out of set– contains(x) tests if x in set
Art of Multiprocessor Programming 3
List-Based Sets
public interface Set<T> { public boolean add(T x); public boolean remove(T x); public boolean contains(T x);}
Art of Multiprocessor Programming 4
List Node
public class Node { public T item; public int key; public Node next;}
Art of Multiprocessor Programming 5
The List-Based Set
a b c
Sorted with Sentinel nodes(min & max possible keys)
-∞
+∞
Art of Multiprocessor Programming 6
Invariants
• Sentinel nodes– tail reachable from head
• Sorted• No duplicates
Art of Multiprocessor Programming 7
This Lecture
• Introduce three “patterns”– Bag of tricks …– Methods that work more than once …
Art of Multiprocessor Programming 8
This Lecture
• Introduce three “patterns”– Bag of tricks …– Methods that work more than once …
• For highly-concurrent objects– Concurrent access– More threads, more throughput
Art of Multiprocessor Programming 9
First:Optimistic Synchronization
• Search without locking …• If you find it, lock and check …
– OK: we are done– Oops: start over
• Evaluation– Usually cheaper than locking, but– Mistakes are expensive
Art of Multiprocessor Programming 10
Second: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 11
Third: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 12
Drawbacks of fine-grained locking
• Better than coarse-grained lock– Threads can traverse in parallel
• Still not ideal– Long chain of acquire/release– Inefficient
Art of Multiprocessor Programming 13
Optimistic Synchronization
• Find nodes without locking• Lock nodes• Check that everything is OK
Art of Multiprocessor Programming 14
Optimistic: Traverse without Locking
b d ea
add(c)
Aha!
Art of Multiprocessor Programming 15
Optimistic: Lock and Load
b d ea
add(c)
Art of Multiprocessor Programming 16
Optimistic: Lock and Load
b d ea
add(c)
c
Art of Multiprocessor Programming 17
What could go wrong?
b d ea
add(c)
Aha!
Art of Multiprocessor Programming 18
What could go wrong?
b d ea
add(c)
Art of Multiprocessor Programming 19
What could go wrong?
b d ea
remove(b)
Art of Multiprocessor Programming 20
What could go wrong?
b d ea
remove(b)
Art of Multiprocessor Programming 21
What could go wrong?
b d ea
add(c)
Art of Multiprocessor Programming 22
What could go wrong?
b d ea
add(c)
c
Art of Multiprocessor Programming 23
What could go wrong?
d ea
add(c) Uh-oh
Art of Multiprocessor Programming 24
Validate – Part 1
b d ea
add(c)
Yes, b still
reachable from head
Art of Multiprocessor Programming 25
What Else Could Go Wrong?
b d ea
add(c)
Aha!
Art of Multiprocessor Programming 26
What Else Coould Go Wrong?
b d ea
add(c)
add(b’)
Art of Multiprocessor Programming 27
What Else Coould Go Wrong?
b d ea
add(c)
add(b’)b’
Art of Multiprocessor Programming 28
What Else Could Go Wrong?
b d ea
add(c)
b’
Art of Multiprocessor Programming 29
What Else Could Go Wrong?
b d ea
add(c)
c
Art of Multiprocessor Programming 30
Validate Part 2(while holding locks)
b d ea
add(c)
Yes, b still
points to d
Art of Multiprocessor Programming 31
Optimistic: Linearization Point
b d ea
add(c)
c
Art of Multiprocessor Programming 32
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
Art of Multiprocessor Programming 33
Unsuccessful Remove
a b d e
remove(c)
Aha!
Art of Multiprocessor Programming 34
Validate (1)
a b d e
Yes, b still reachable from head
remove(c)
Art of Multiprocessor Programming 35
Validate (2)
a b d e
remove(c)
Yes, b still points to
d
Art of Multiprocessor Programming 36
OK Computer
a b d e
remove(c)
return false
Art of Multiprocessor Programming 37
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
Art of Multiprocessor Programming 38
Validationprivate 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 39
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
Art of Multiprocessor Programming 40
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
Begin at the beginning
Art of Multiprocessor Programming 41
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
Art of Multiprocessor Programming 42
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
Art of Multiprocessor Programming 43
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?
Art of Multiprocessor Programming 44
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
Art of Multiprocessor Programming 45
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;}
ValidationPredecessor not
reachable
Art of Multiprocessor Programming 46
Remove: searchingpublic 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 47
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; } …
Remove: searching
Search key
Art of Multiprocessor Programming 48
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; } …
Remove: searching
Retry on synchronization conflict
Art of Multiprocessor Programming 49
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; } …
Remove: searching
Examine predecessor and current nodes
Art of Multiprocessor Programming 50
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; } …
Remove: searching
Search by key
Art of Multiprocessor Programming 51
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; } …
Remove: searching
Stop if we find item
Art of Multiprocessor Programming 52
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; } …
Remove: searching
Move along
Art of Multiprocessor Programming 53
On Exit from Loop
• If item is present– curr holds item– pred just before curr
• If item is absent– curr has first higher key– pred just before curr
• Assuming no synchronization problems
Art of Multiprocessor Programming 54
Remove Methodtry { 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 55
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();
}}}
Remove Method
Always unlock
Art of Multiprocessor Programming 56
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();
}}}
Remove Method
Lock both nodes
Art of Multiprocessor Programming 57
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();
}}}
Remove Method
Check for synchronization
conflicts
Art of Multiprocessor Programming 58
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();
}}}
Remove Method
target found, remove node
Art of Multiprocessor Programming 59
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();
}}}
Remove Method
target not found
Art of Multiprocessor Programming 60
Optimistic List
• Limited hot-spots– Targets of add(), remove(), contains()– No contention on traversals
• Moreover– Traversals are wait-free– Food for thought …
Art of Multiprocessor Programming 61
So Far, So Good
• Much less lock acquisition/release– Performance– Concurrency
• Problems– Need to traverse list twice– contains() method acquires locks
Art of Multiprocessor Programming 62
Evaluation
• Optimistic is effective if– cost of scanning twice without locks
is less than– cost of scanning once with locks
• Drawback– contains() acquires locks– 90% of calls in many apps
Art of Multiprocessor Programming 63
Lazy List
• Like optimistic, except– Scan once– contains(x) never locks …
• Key insight– Removing nodes causes trouble– Do it “lazily”
Art of Multiprocessor Programming 64
Lazy List
• remove()– 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 65
Lazy Removal
aa b c d
c
Art of Multiprocessor Programming 66
Lazy Removal
aa b d
Present in list
c
Art of Multiprocessor Programming 67
Lazy Removal
aa b d
Logically deleted
Art of Multiprocessor Programming 68
Lazy Removal
aa b c d
Physically deleted
Art of Multiprocessor Programming 69
Lazy Removal
aa b d
Physically deleted
Art of Multiprocessor Programming 70
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.
Art of Multiprocessor Programming 71
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 72
Business as Usual
a b c
Art of Multiprocessor Programming 73
Business as Usual
a b c
Art of Multiprocessor Programming 74
Business as Usual
a b c
Art of Multiprocessor Programming 75
Business as Usual
a b c
remove(b)
Art of Multiprocessor Programming 76
Business as Usual
a b c
a not marked
Art of Multiprocessor Programming 77
Business as Usual
a b c
a still points to b
Art of Multiprocessor Programming 78
Business as Usual
a b c
Logical
delete
Art of Multiprocessor Programming 79
Business as Usual
a b c
physical
delete
Art of Multiprocessor Programming 80
Business as Usual
a b c
Art of Multiprocessor Programming 81
Validationprivate boolean validate(Node pred, Node curr) { return !pred.marked && !curr.marked && pred.next == curr); }
Art of Multiprocessor Programming 82
private boolean validate(Node pred, Node curr) { return !pred.marked && !curr.marked && pred.next == curr); }
List Validate Method
Predecessor not Logically removed
Art of Multiprocessor Programming 83
private boolean validate(Node pred, Node curr) { return !pred.marked && !curr.marked && pred.next == curr); }
List Validate Method
Current not Logically removed
Art of Multiprocessor Programming 84
private boolean validate(Node pred, Node curr) { return !pred.marked && !curr.marked && pred.next == curr); }
List Validate Method
Predecessor stillPoints to current
Art of Multiprocessor Programming 85
Removetry { 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 86
Removetry { 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 87
Removetry { 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 88
Removetry { 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 89
Removetry { 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 90
Containspublic 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;}
Art of Multiprocessor Programming 91
Containspublic 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;}
Start at the head
Art of Multiprocessor Programming 92
Containspublic 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;}
Search key range
Art of Multiprocessor Programming 93
Containspublic 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 94
Containspublic 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;}
Present and undeleted?
Art of Multiprocessor Programming 95
Summary: Wait-free Contains
a 0 0 0a b c 0e1d
Use Mark bit + list ordering 1. Not marked in the set2. Marked or missing not in the set
Art of Multiprocessor Programming 96
Lazy List
a 0 0 0a b c 0e1d
Lazy add() and remove() + Wait-free contains()
Art of Multiprocessor Programming 97
Evaluation
• Good:– contains() doesn’t lock– In fact, its wait-free! – Good because typically high %
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 98
Traffic Jam
• Any concurrent data structure based on mutual exclusion has a weakness
• If one thread– Enters critical section– And “eats the big muffin”
• Cache miss, page fault, descheduled …– Everyone else using that lock is stuck!– Need to trust the scheduler….
Art of Multiprocessor Programming 99
Reminder: Lock-Free Data Structures
• No matter what …– Guarantees minimal progress in any
execution– i.e. Some thread will always complete
a method call– Even if others halt at malicious times– Implies that implementation can’t use
locks
Art of Multiprocessor Programming 100
Lock-free Lists
• Next logical step– Wait-free contains()– lock-free add() and remove()
• Use only compareAndSet()– What could go wrong?
Art of Multiprocessor Programming 101
Remove Using CAS
a 0 0 0a b c 0e1c
Logical Removal =Set Mark Bit
PhysicalRemovalCAS pointer
Use CAS to verify pointer is correct
Not enough!
Art of Multiprocessor Programming 102
Problem…
a 0 0 0a b c 0e1c
Logical Removal =Set Mark Bit
PhysicalRemovalCAS
0dProblem: d not added to list…Must Prevent manipulation of removed node’s pointer
Node added BeforePhysical Removal CAS
Art of Multiprocessor Programming 103
The Solution: Combine Bit and Pointer
a 0 0 0a b c 0e1c
Logical Removal =Set Mark Bit
PhysicalRemovalCAS
0d
Mark-Bit and Pointerare CASed together(AtomicMarkableReference)
Fail CAS: Node not added after logical Removal
Art of Multiprocessor Programming 104
Solution
• Use AtomicMarkableReference• Atomically
– Swing reference and– Update flag
• Remove in two steps– Set mark bit in next field– Redirect predecessor’s pointer
Art of Multiprocessor Programming 105
Marking a Node
• AtomicMarkableReference class– Java.util.concurrent.atomic package
address F
mark bit
Reference
Art of Multiprocessor Programming 106
Extracting Reference & Mark
Public Object get(boolean[] marked);
Art of Multiprocessor Programming 107
Extracting Reference & Mark
Public Object get(boolean[] marked);
Returns reference
Returns mark at array index
0!
Art of Multiprocessor Programming 108
Extracting Mark Only
public boolean isMarked();
Value of mark
Art of Multiprocessor Programming 109
Changing State
Public boolean compareAndSet( Object expectedRef, Object updateRef, boolean expectedMark, boolean updateMark);
Art of Multiprocessor Programming 110
Changing State
Public boolean compareAndSet( Object expectedRef, Object updateRef, boolean expectedMark, boolean updateMark);
If this is the current reference
…
And this is the current mark …
Art of Multiprocessor Programming 111
Changing State
Public boolean compareAndSet( Object expectedRef, Object updateRef, boolean expectedMark, boolean updateMark);
…then change to this new reference …
… and this new mark
Art of Multiprocessor Programming 112
Changing State
public boolean attemptMark( Object expectedRef, boolean updateMark);
Art of Multiprocessor Programming 113
Changing State
public boolean attemptMark( Object expectedRef, boolean updateMark);
If this is the current reference …
Art of Multiprocessor Programming 114
Changing State
public boolean attemptMark( Object expectedRef, boolean updateMark);
.. then change to this new mark.
Art of Multiprocessor Programming 115
Removing a Node
a b c d
remove c
CAS
Art of Multiprocessor Programming 116
Removing a Node
a b d
remove b
remove c
cCASCAS
failed
Art of Multiprocessor Programming 117
Removing a Node
a b d
remove b
remove c
c
Art of Multiprocessor Programming 118
Removing a Node
a d
remove b
remove c
Art of Multiprocessor Programming 119
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)
Art of Multiprocessor Programming 120
Lock-Free Traversal(only Add and Remove)
a b c dCAS
Uh-oh
pred currpred curr
Art of Multiprocessor Programming 121
The Window Class
class Window { public Node pred; public Node curr; Window(Node pred, Node curr) { this.pred = pred; this.curr = curr; }}
Art of Multiprocessor Programming 122
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
Art of Multiprocessor Programming 123
Using the Find Method
Window window = find(head, key); Node pred = window.pred; curr = window.curr;
Art of Multiprocessor Programming 124
Using the Find Method
Window window = find(head, key); Node pred = window.pred; curr = window.curr;
Find returns window
Art of Multiprocessor Programming 125
Using the Find Method
Window window = find(head, key); Node pred = window.pred; curr = window.curr;
Extract pred and curr
Art of Multiprocessor Programming© Herlihy-Shavit 2007
126
The Find Method
Window window = find(item);
At some instant,
pred curr succ
itemor …
Art of Multiprocessor Programming© Herlihy-Shavit 2007
127
The Find Method
Window window = find(item);
At some instant,
predcurr= null
succ
item not in list
Art of Multiprocessor Programming 128
Removepublic boolean remove(T item) {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;}}}
Art of Multiprocessor Programming 129
Removepublic boolean remove(T item) {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
Art of Multiprocessor Programming 130
Removepublic boolean remove(T item) {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
Art of Multiprocessor Programming 131
Removepublic boolean remove(T item) {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 …
Art of Multiprocessor Programming 132
Removepublic boolean remove(T item) {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
Art of Multiprocessor Programming 133
Removepublic boolean remove(T item) {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
Art of Multiprocessor Programming 134
Removepublic boolean remove(T item) {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
Art of Multiprocessor Programming 135
Addpublic boolean add(T item) { 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(item); node.next = new AtomicMarkableRef(curr, false); if (pred.next.compareAndSet(curr, node, false, false)) {return true;}}}}
Art of Multiprocessor Programming 136
Addpublic boolean add(T item) { 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(item); node.next = new AtomicMarkableRef(curr, false); if (pred.next.compareAndSet(curr, node, false, false)) {return true;}}}} Item already there.
Art of Multiprocessor Programming 137
Addpublic boolean add(T item) { 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(item); node.next = new AtomicMarkableRef(curr, false); if (pred.next.compareAndSet(curr, node, false, false)) {return true;}}}}
create new node
Art of Multiprocessor Programming 138
Addpublic boolean add(T item) { 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(item); node.next = new AtomicMarkableRef(curr, false); if (pred.next.compareAndSet(curr, node, false, false)) {return true;}}}}
Install new node, else retry loop
Art of Multiprocessor Programming 139
Wait-free Contains
public boolean contains(Tt item) { boolean marked; int key = item.hashCode(); Node curr = this.head; while (curr.key < key) curr = curr.next; Node succ = curr.next.get(marked); return (curr.key == key && !marked[0]) }
Art of Multiprocessor Programming 140
Wait-free Contains
public boolean contains(T item) { boolean marked; int key = item.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 is that we get and check
marked
Art of Multiprocessor Programming 141
Lock-free Findpublic 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; }}}
Art of Multiprocessor Programming 142
Lock-free Findpublic 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 overLock-Free
because we start over
only if someone
else makes progress
Art of Multiprocessor Programming 143
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
Art of Multiprocessor Programming 144
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
Art of Multiprocessor Programming 145
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
Art of Multiprocessor Programming 146
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
Art of Multiprocessor Programming 147
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 curr key that is greater or equal, return pred and
curr
Art of Multiprocessor Programming 148
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
Art of Multiprocessor Programming 149
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); }…
Art of Multiprocessor Programming 150
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
Art of Multiprocessor Programming 151
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
Art of Multiprocessor Programming 152
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
Art of Multiprocessor Programming 153
Performance
On 16 node shared memory machineBenchmark throughput of Java List-based Setalgs. Vary % of Contains() method Calls.
Art of Multiprocessor Programming 154
High Contains Ratio
Lock-free Lazy list
Course GrainedFine Lock-coupling
Art of Multiprocessor Programming 155
Low Contains Ratio
Lock-free
Lazy list
Course GrainedFine Lock-coupling
Art of Multiprocessor Programming 156
As Contains Ratio Increases
Lock-free
Lazy list
Course GrainedFine Lock-coupling
% Contains()
Art of Multiprocessor Programming 157
Summary
• Coarse-grained locking• Fine-grained locking• Optimistic synchronization• Lock-free synchronization
Art of Multiprocessor Programming 158
Homework
Projects
• Topic: • good: choose from among our suggestions• better: define your own project
• On your own or in groups of two
• Pick a project by: before Christmas• Progress report 1: January 15th (2 pages)• Presentation and final report: January 27th and
February 3rd (final report: 4 pages)
Project 1: Irregular data parallelism
Cavity
Example: Delaunay mesh refinement
Effects of updates are local, but not bounded statically (“irregular”).
Can we still exploit locality for parallelism?
Project 1: Irregular data parallelism
http://iss.ices.utexas.edu/lonestar/index.html
Locality of effects: Mesh retriangulation
Project 1: Irregular data parallelismLonestar benchmark suite:http://iss.ices.utexas.edu/lonestar/index.html
1. Barnes-Hut N-body Simulation
2. Delaunay Mesh refinement3. Focused communities4. Delaunay triangulation5. …
Project: 1. pick one of these applications, 2. find a good (possibly novel) way of parallelizing it, 3. implement it (by modifying the sequential implementation provided in Lonestar benchmarks), 4. confirm improvement in running time by experimentation.
Project 2: Deductive verification: proving a concurrent data structure correct
1. Pick an implementation of a concurrent data structure: a stack, a queue, a set, ..
2. Pick a theorem prover or a verification tool: for example: PVS or QED
3. Prove that the implementation is linearizable
3 5 7 9
P1: remove(7)
P2: remove(5)
Project 2: Deductive verification: proving a concurrent data structure correct
References:1. R. Colvin, L. Groves, V. Luchangco, M. Moir: Formal Verification of a Lazy
Concurrent List-Based Set Algorithm. CAV 20062. T. Elmas, S. Qadeer, A. Sezgin, O. Subasi, S. Tasiran: Simplifying
Linearizability Proofs with Reduction and Abstraction. TACAS 2010.
3 5 7 9
P1: remove(7)
P2: remove(5)
Project 3:Performance measurement/ performance model for concurrent programs
1. Pick a problem with at least three-four different solutions1. Lock implementations2. Data structures: queues, stacks, sets…
2. Examine the performance of the solutions in different settings:1. small number of threads vs large number of threads2. 2 cores, small amount of memory (laptop) vs 8 cores, large
memory/cache (server)3. different usage models4. input that generates little vs input that generates lots of contention
3a. Find a hybrid solution that works well in a particular settingor
3b. Find a performance model that explains the data
Project 4: Your own!
Art of Multiprocessor Programming 167
Summary• Mutual Exclusion Algorithms (Peterson,
Filter, Bakery), Lower Bounds (Covering proof)
• Correcteness conditions: 1. Linearizability, 2. Sequential consistency
• Proving Linearizability: 1. Rely-Guarantee, 2. Reductions (left movers, right movers)
• Case study: Concurrent Lists (coarse-grained, fine-grained, optimistic, lazy, lock-free implementations)
top related