Download presentation
Presentation is loading. Please wait.
Published byAshlee Lawson Modified over 9 years ago
1
Concurrent Tries with Efficient Non-blocking Snapshots Aleksandar Prokopec Phil Bagwell Martin Odersky École Polytechnique Fédérale de Lausanne Nathan Bronson Stanford
2
Motivation val numbers = getNumbers() // compute square roots numbers foreach { entry => x = entry.root n = entry.number entry.root = 0.5 * (x + n / x) if (abs(entry.root - x) < eps) numbers.remove(entry) }
3
Hash Array Mapped Tries (HAMT)
4
0 = 000000 2
5
Hash Array Mapped Tries (HAMT) 0
6
0 16 = 010000 2
7
Hash Array Mapped Tries (HAMT) 016
8
Hash Array Mapped Tries (HAMT) 016 4 = 000100 2
9
Hash Array Mapped Tries (HAMT) 16 0 4 = 000100 2
10
Hash Array Mapped Tries (HAMT) 16 04
11
Hash Array Mapped Tries (HAMT) 16 04 12 = 001100 2
12
Hash Array Mapped Tries (HAMT) 16 04 12 = 001100 2
13
Hash Array Mapped Tries (HAMT) 16 0412
14
Hash Array Mapped Tries (HAMT) 1633 0412
15
Hash Array Mapped Tries (HAMT) 1633 0412 48
16
Hash Array Mapped Tries (HAMT) 16 0412 48 3337
17
Hash Array Mapped Tries (HAMT) 16 412 48 3337 03
18
Hash Array Mapped Tries (HAMT) 4121620253337 01893 4857
19
Immutable HAMT used as immutable maps in functional languages 4121620253337 01893
20
Immutable HAMT updates rewrite path from root to leaf 4121620253337 01893 412 8911 insert(11)
21
Immutable HAMT updates rewrite path from root to leaf 4121620253337 01893 412 8911 insert(11) efficient updates - log k (n)
22
Node compression 4857 48571010 48571010 485710 BITPOP(((1 > lev) & 1F)) – 1) & BMP)
23
Node compression 4857 48571010 48571010 485710 4857
24
Ctrie Can mutable HAMT be modified to be thread-safe?
25
Ctrie insert 49121620253337 013 4857 17 = 010001 2
26
Ctrie insert 49121620253337 013 4857 17 = 010001 2 1617 1) allocate
27
Ctrie insert 491220253337 013 4857 17 = 010001 2 1617 2) CAS
28
Ctrie insert 491220253337 013 4857 17 = 010001 2 1617
29
Ctrie insert 49123337 013 4857 18 = 010010 2 1617 2025
30
Ctrie insert 49123337 013 4857 18 = 010010 2 1617 2025 1) allocate 161718
31
Ctrie insert 49123337 013 4857 18 = 010010 2 2025 2) CAS 161718
32
Ctrie insert 49123337 013 4857 18 = 010010 2 2025 2) CAS 161718 Unless…
33
Ctrie insert 49123337 013 4857 18 = 010010 2 1617 2025 T1-1) allocate 161718 Unless… 28 = 011100 2 T1 T2
34
Ctrie insert 4912 013 18 = 010010 2 1617 2025 T1-1) allocate 161718 Unless… 28 = 011100 2 T1 T2 202528 T2-1) allocate
35
Ctrie insert 4912 013 18 = 010010 2 1617 2025 T1-1) allocate 161718 28 = 011100 2 T1 T2 202528 T2-2) CAS
36
Ctrie insert 4912 013 18 = 010010 2 1617 2025 T1-2) CAS 161718 28 = 011100 2 T1 T2 202528 T2-2) CAS
37
Ctrie insert 4912 013 18 = 010010 2 1617 2025 161718 28 = 011100 2 T1 T2 202528 Lost insert!
38
Ctrie insert – 2 nd attempt 4912 0131617 2025 Solution: I-nodes
39
Ctrie insert – 2 nd attempt 4912 0131617 2025 18 = 010010 2 28 = 011100 2 T1 T2
40
Ctrie insert – 2 nd attempt 4912 0131617 T1 T2 2025 18 = 010010 2 28 = 011100 2 161718 202528 T2-1) allocate T1-1) allocate
41
Ctrie insert – 2 nd attempt 4912 0131617 T1 T2 2025 161718 202528 T2-2) CAS T1-2) CAS
42
Ctrie insert – 2 nd attempt 4912 013161718 202528
43
Ctrie insert – 2 nd attempt 4912 013161718 202528 Idea: once added to the Ctrie, I-nodes remain present.
44
Ctrie insert – 2 nd attempt 4912 013161718 202528 Remove operation supported as well - details in the paper.
45
Ctrie size 4912 013161718 202528
46
Ctrie size 4912 013161718 202528 size = 0
47
Ctrie size 4912 013161718 202528 size = 0
48
Ctrie size 4912 013161718 202528 size = 0
49
Ctrie size 4912 013161718 202528 size = 0
50
Ctrie size 4912 013161718 202528 size = 1
51
Ctrie size 4912 013161718 202528 size = 2
52
Ctrie size 4912 013161718 202528 size = 3
53
Ctrie size 4912 013161718 202528 size = 5
54
Ctrie size 4912 013161718 202528 size = 5 actual size = 12
55
Ctrie size 4912 013161718 202528 size = 5 01 actual size = 12
56
Ctrie size 4912 013161718 202528 size = 5 01 CAS actual size = 11
57
Ctrie size 4912 161718 202528 size = 5 01 actual size = 11
58
Ctrie size 4912 161718 202528 size = 6 01 actual size = 11
59
Ctrie size 4912 161718 202528 size = 6 01 actual size = 11 19
60
Ctrie size 4912 161718 202528 size = 6 01 actual size = 11 161718 19
61
Ctrie size 4912 161718 202528 size = 6 01 actual size = 12 161718 19 CAS
62
Ctrie size 4912202528 size = 6 01 actual size = 12 161718 19
63
Ctrie size 4912202528 size = 6 01 actual size = 12 161718 19
64
Ctrie size 4912202528 size = 7 01 actual size = 9 161718 19
65
Ctrie size 4912202528 size = 8 01 actual size = 12 161718 19
66
Ctrie size 4912202528 size = 9 01 actual size = 12 161718 19
67
Ctrie size 4912202528 size = 10 01 actual size = 12 161718 19
68
Ctrie size 4912202528 size = 11 01 actual size = 12 161718 19
69
Ctrie size 4912202528 size = 12 01 actual size = 12 161718 19
70
Ctrie size 4912202528 size = 13 01 actual size = 12 161718 19
71
Ctrie size 4912202528 size = 13 01 actual size = 12 161718 19 But the size was never 13!
72
Global state information 4912202528 01 161718 19 size find filter iterator
73
Global state information 4912202528 01 161718 19 size find filter iterator snapshot
74
Snapshot using locks 4912202528 01 161718 19
75
Snapshot using locks 4912202528 01 161718 19 copy expensive
76
Snapshot using locks 4912202528 01 161718 19 copy expensive not lock-free
77
Snapshot using locks 4912202528 01 161718 19 copy expensive not lock-free can insert or remove remain lock-free? 01 2 CAS
78
Snapshot using locks 4912202528 01 161718 19 copy expensive not lock-free can insert or remove remain lock-free? 01 2 CAS
79
Snapshot using logs 4912202528 01 161718 19 keep a linked list of previous values in each I-node
80
Snapshot using logs 4912202528 01 161718 19 01 2 keep a linked list of previous values in each I-node
81
Snapshot using logs 4912202528 01 161718 19 keep a linked list of previous values in each I-node when is it safe to delete old entries? 01 2
82
Snapshot using immutability 4912202528 01 161718 19 root
83
Snapshot using immutability 4912202528 01 161718 19 #1 root
84
Snapshot using immutability 4912202528 01 161718 19 #1 snapshot! root
85
Snapshot using immutability 4912202528 01 161718 19 #1 snapshot! #2 root 1) create new I-node at #2
86
Snapshot using immutability 4912202528 01 161718 19 #1 snapshot! #2 root 2) set snapshot snapshot #1
87
Snapshot using immutability 4912202528 01 161718 19 #1 snapshot! #2 root 3) CAS root to new I-node snapshot #1
88
Snapshot using immutability 4912202528 01 161718 19 #1 subsequent insert #2 rootsnapshot #1 2
89
Snapshot using immutability 4912202528 01 161718 19 #1 subsequent insert #2 rootsnapshot #1 2 generation #2 - ok!
90
Snapshot using immutability 4912202528 01 161718 19 #1 subsequent insert #2 rootsnapshot #1 2 generation #1 not ok, too old!
91
Snapshot using immutability 4912202528 01 161718 19 #1 subsequent insert #2 root 1) create updated node at #2 snapshot #1 2 #2
92
Snapshot using immutability 4912202528 01 161718 19 #1 subsequent insert #2 root 2) CAS to the updated node snapshot #1 2 #2
93
Snapshot using immutability 4912202528 01 161718 19 #1 subsequent insert #2 rootsnapshot #1 2 #2 #1 too old!
94
Snapshot using immutability 4912202528 01 161718 19 #1 subsequent insert #2 rootsnapshot #1 2 #2 4912 #2 1) create updated node at #2
95
Snapshot using immutability 4912202528 01 161718 19 #1 subsequent insert #2 rootsnapshot #1 2 #2 4912 #2 2) CAS
96
Snapshot using immutability 4912202528 01 161718 19 #1 subsequent insert #2 rootsnapshot #1 #2 4912 #2 01 2 finally, create a new leaf and CAS
97
Snapshot using immutability 4912202528 01 161718 19 #1 another insert #2 rootsnapshot #1 #2 4912 #2 01 2 3
98
Snapshot using immutability 4912202528 01 161718 19 #1 another insert #2 rootsnapshot #1 #2 4912 #2 01 2 01 23
99
Snapshot using immutability 4912202528 01 161718 19 #1 But... this won't really work... why? #2 rootsnapshot #1 #2 4912 #2 01 2 01 23
100
Snapshot using immutability 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 2 01 23 T2: remove 19 161718
101
Snapshot using immutability 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 2 01 23 T2: remove 19 161718 CAS
102
Snapshot using immutability 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 2 01 23 T2: remove 19 161718 CAS How to fail this last CAS?
103
Snapshot using immutability 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 2 01 23 T2: remove 19 161718 DCAS How to fail this last CAS? DCAS
104
Snapshot using immutability 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 2 01 23 T2: remove 19 161718 How to fail this last CAS? DCAS - software based DCAS
105
Snapshot using immutability 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 2 01 23 T2: remove 19 161718 How to fail this last CAS? DCAS - software based...creates intermediate objects DCAS
106
GCAS - generation-compare-and-swap 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 23 T2: remove 19 161718 prev 1) set prev field
107
GCAS - generation-compare-and-swap 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 23 T2: remove 19 161718 prev 2) CAS
108
GCAS - generation-compare-and-swap 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 23 T2: remove 19 161718 prev 3) read root generation
109
GCAS - generation-compare-and-swap 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 23 161718 prev 4) if root generation changed CAS prev to FailedNode(prev) FN
110
GCAS - generation-compare-and-swap 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 23 161718 prev 4) if root generation changed CAS prev to FailedNode(prev) FN
111
GCAS - generation-compare-and-swap 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 23 161718 prev 5) CAS to previous value FN
112
GCAS - generation-compare-and-swap 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 23 161718 prev 4) if root generation unchanged CAS prev to null
113
GCAS - generation-compare-and-swap 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 23 161718 4) if root generation unchanged CAS prev to null
114
GCAS - generation-compare-and-swap 4912202528 01 161718 19 #1 #2 rootsnapshot #1 #2 4912 #2 01 23 1) Replace all CAS with GCAS 2) Replace all READ with GCAS_READ (which checks if prev field is null)
115
Snapshot-based iterator def iterator = if (isSnapshot) new Iterator(root) else snapshot().iterator()
116
Snapshot-based size def size = { val sz = 0 val it = iterator while (it.hasNext) sz += 1 sz }
117
Snapshot-based size def size = { val sz = 0 val it = iterator while (it.hasNext) sz += 1 sz } Above is O(n). But, by caching size in nodes - amortized O(log k n)! (see source code)
118
Snapshot-based atomic clear def clear() = { val or = READ(root) val nr = new INode(new Gen) if (!CAS(root, or, nr)) clear() } (roughly)
119
Evaluation - quad core i7
120
Evaluation – UltraSPARC T2
121
Evaluation – 4x 8-core i7
122
Evaluation – snapshot
123
Conclusion snapshots are linearizable and lock-free snapshots take constant time snapshots are horizontally scalable snapshots add a non-significant overhead to the algorithm if they aren't used the approach may be applicable to tree-based lock-free data-structures in general (intuition)
124
Thank you!
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.