Professional Documents
Culture Documents
Non-blocking Snapshots
Aleksandar Prokopec
Phil Bagwell
Martin Odersky
cole Polytechnique Fdrale de Lausanne
Nathan Bronson
Stanford
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)
}
0 = 0000002
16 = 0100002
0 16
4 = 0001002
0 16
16
4 = 0001002
16
0 4
16
12 = 0011002
0 4
16
12 = 0011002
0 4
16
0 4
12
16 33
0 4
12
16 33 48
0 4
12
16
0 4
12
48
33 37
16
12
48
33 37
0 1
12
8 9
16 20 25
33 37
48
57
Immutable HAMT
used as immutable maps in functional languages
0 1
12
16 20 25
8 9
33 37
Immutable HAMT
updates rewrite path from root to leaf
insert(11)
0 1
12
16 20 25
8 9
33 37
12
8 9
11
Immutable HAMT
updates rewrite path from root to leaf
insert(11)
0 1
12
16 20 25
8 9
33 37
12
8 9
11
Node compression
48
57
1 0 1 0
48
57
1 0 1 0
48 57
10 48 57
Node compression
48
57
1 0 1 0
48
57
1 0 1 0
48 57
10 48 57
48 57
Ctrie
thread-safe?
Ctrie insert
4 9 12
0 1 3
16 20 25
33 37
48 57
17 = 0100012
Ctrie insert
4 9 12
0 1 3
16 20 25
16 17
1) allocate
33 37
48 57
17 = 0100012
Ctrie insert
4 9 12
0 1 3
20 25
16 17
33 37
48 57
17 = 0100012
2) CAS
Ctrie insert
4 9 12
0 1 3
20 25
16 17
33 37
48 57
17 = 0100012
Ctrie insert
4 9 12
0 1 3
20 25
33 37
48 57
16 17
18 = 0100102
Ctrie insert
4 9 12
0 1 3
20 25
16 17
33 37
16 17 18
48 57
1) allocate
18 = 0100102
Ctrie insert
4 9 12
0 1 3
20 25
2) CAS
33 37
48 57
16 17 18
18 = 0100102
Ctrie insert
Unless
4 9 12
0 1 3
20 25
2) CAS
33 37
48 57
16 17 18
18 = 0100102
Ctrie insert
28 = 0111002
Unless
4 9 12
0 1 3
20 25
16 17
T2
33 37
16 17 18
48 57
T1-1) allocate
T1
18 = 0100102
Ctrie insert
Unless
4 9 12
0 1 3
20 25
16 17
28 = 0111002
T2
20 25 28
16 17 18
T2-1) allocate
T1-1) allocate
T1
18 = 0100102
Ctrie insert
T2-2) CAS
4 9 12
0 1 3
20 25
16 17
28 = 0111002
T2
20 25 28
16 17 18
T1-1) allocate
T1
18 = 0100102
Ctrie insert
T2-2) CAS
4 9 12
0 1 3
20 25
16 17
28 = 0111002
T2
20 25 28
16 17 18
T1
T1-2) CAS
18 = 0100102
Ctrie insert
28 = 0111002
T2
4 9 12
0 1 3
20 25 28
16 17
T1
20 25
18 = 0100102
16 17 18
Lost insert!
Solution: I-nodes
4 9 12
0 1 3
20 25
16 17
4 9 12
0 1 3
T2
20 25
16 17
18 = 0100102
T1
4 9 12
0 1 3
20 25
16 17
20 25 28
16 17 18
18 = 0100102
T2
T2-1) allocate
T1-1) allocate
T1
T2-2) CAS
4 9 12
20 25
20 25 28
T1-2) CAS
0 1 3
16 17
16 17 18
T1
4 9 12
0 1 3
20 25 28
16 17 18
4 9 12
0 1 3
20 25 28
16 17 18
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
size = 0
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
size = 0
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
size = 0
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
size = 0
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
size = 1
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
size = 2
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
size = 3
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
size = 5
4 9 12
0 1 3
20 25 28
16 17 18
Ctrie size
size = 5
4 9 12
0 1 3
actual size = 12
20 25 28
16 17 18
Ctrie size
size = 5
4 9 12
0 1
0 1 3
actual size = 12
20 25 28
16 17 18
Ctrie size
size = 5
4 9 12
20 25 28
CAS
0 1
0 1 3
actual size = 11
16 17 18
Ctrie size
size = 5
4 9 12
0 1
actual size = 11
20 25 28
16 17 18
Ctrie size
size = 6
4 9 12
0 1
actual size = 11
20 25 28
16 17 18
Ctrie size
size = 6
4 9 12
0 1
20 25 28
16 17 18
19
actual size = 11
Ctrie size
size = 6
4 9 12
0 1
actual size = 11
20 25 28
16 17 18
16 17 18 19
Ctrie size
size = 6
4 9 12
20 25 28
CAS
0 1
actual size = 12
16 17 18
16 17 18 19
Ctrie size
size = 6
4 9 12
0 1
actual size = 12
20 25 28
16 17 18 19
Ctrie size
size = 6
4 9 12
0 1
actual size = 12
20 25 28
16 17 18 19
Ctrie size
size = 7
4 9 12
0 1
actual size = 9
20 25 28
16 17 18 19
Ctrie size
size = 8
4 9 12
0 1
actual size = 12
20 25 28
16 17 18 19
Ctrie size
size = 9
4 9 12
0 1
actual size = 12
20 25 28
16 17 18 19
Ctrie size
size = 10
4 9 12
0 1
actual size = 12
20 25 28
16 17 18 19
Ctrie size
size = 11
4 9 12
0 1
actual size = 12
20 25 28
16 17 18 19
Ctrie size
size = 12
4 9 12
0 1
actual size = 12
20 25 28
16 17 18 19
Ctrie size
size = 13
4 9 12
0 1
actual size = 12
20 25 28
16 17 18 19
Ctrie size
size = 13
4 9 12
0 1
actual size = 12
20 25 28
16 17 18 19
4 9 12
0 1
20 25 28
16 17 18 19
size
find
filter
iterator
4 9 12
0 1
20 25 28
size
find
filter
iterator
snapshot
16 17 18 19
4 9 12
0 1
20 25 28
16 17 18 19
0 1
20 25 28
16 17 18 19
0 1
20 25 28
16 17 18 19
4 9 12
0 1
CAS
0 1 2
20 25 28
16 17 18 19
copy expensive
not lock-free
can insert or
remove remain
lock-free?
4 9 12
0 1
CAS
0 1 2
20 25 28
16 17 18 19
copy expensive
not lock-free
can insert or
remove remain
lock-free?
4 9 12
0 1
20 25 28
16 17 18 19
4 9 12
0 1 2
0 1
20 25 28
16 17 18 19
4 9 12
0 1 2
0 1
20 25 28
16 17 18 19
4 9 12
0 1
20 25 28
16 17 18 19
#1
4 9 12
#1
0 1
#1
20 25 28
#1
16 17 18 19
#1
4 9 12
#1
0 1
#1
20 25 28
#1
16 17 18 19
snapshot!
#1
#1
4 9 12
#1
0 1
#1
20 25 28
#1
16 17 18 19
snapshot!
root
2) set snapshot
#2
#1
#1
4 9 12
#1
0 1
#1
20 25 28
#1
16 17 18 19
snapshot!
root
#1
#1
4 9 12
#1
0 1
#1
20 25 28
#1
16 17 18 19
snapshot!
root
#2
#1
#1
4 9 12
#1
#1
20 25 28
#1
16 17 18 19
0 1
subsequent insert
root
#2
#1
generation #2 - ok!
#1
4 9 12
#1
#1
20 25 28
#1
16 17 18 19
0 1
subsequent insert
root
#2
#1
generation #1
not ok, too old!
4 9 12
#1
#1
#1
20 25 28
#1
16 17 18 19
0 1
subsequent insert
root
#2
#1
4 9 12
#1
#1
#2
#2
20 25 28
#1
16 17 18 19
0 1
subsequent insert
root
#1
#1
4 9 12
#1
#1
#2
#2
20 25 28
#1
16 17 18 19
0 1
subsequent insert
root
#2
#1
#1
4 9 12
#1 too old!
#1
#1
#2
#2
20 25 28
#1
16 17 18 19
0 1
subsequent insert
root
#2
#1
#1
4 9 12
#1
#1
20 25 28
#1
#2
#2
4 9 12
#2
16 17 18 19
0 1
subsequent insert
root
#2
#1
#1
4 9 12
#1
#1
2) CAS
20 25 28
#1
#2
#2
4 9 12
#2
16 17 18 19
0 1
subsequent insert
root
#2
#1
#1
4 9 12
#1
0 1
#1
20 25 28
#1
16 17 18 19
#2
#2
4 9 12
#2
0 1 2
subsequent insert
root
#2
#1
#1
4 9 12
#1
0 1
#1
20 25 28
#1
16 17 18 19
#2
#2
4 9 12
#2
0 1 2
another insert
root
#2
#1
#1
4 9 12
#1
0 1
#1
20 25 28
#1
16 17 18 19
#2
#2
4 9 12
#2
0 1 2
another insert
0 1 2 3
root
#2
#1
#1
4 9 12
#1
0 1
#1
20 25 28
#1
16 17 18 19
#2
#2
4 9 12
#2
0 1 2
0 1 2 3
root
#2
#1
#1
4 9 12
#1
#1
20 25 28
#1
16 17 18 19
0 1
16 17 18
T2: remove 19
#2
#2
4 9 12
#2
0 1 2
0 1 2 3
root
#2
#1
#1
4 9 12
#1
CAS
#1
20 25 28
#1
16 17 18 19
0 1
16 17 18
T2: remove 19
#2
#2
4 9 12
#2
0 1 2
0 1 2 3
root
#2
#1
#1
4 9 12
#1
CAS
#1
20 25 28
#1
16 17 18 19
0 1
#2
#2
4 9 12
#2
0 1 2
0 1 2 3
16 17 18
T2: remove 19
root
#2
#1
#1
4 9 12
#1
DCAS
#1
20 25 28
#1
16 17 18 19
0 1
#2
#2
4 9 12
#2
0 1 2
0 1 2 3
16 17 18
T2: remove 19
root
#2
#1
#1
4 9 12
#1
DCAS
#1
20 25 28
#1
16 17 18 19
0 1
#2
#2
4 9 12
#2
0 1 2
0 1 2 3
16 17 18
T2: remove 19
root
#2
#1
#1
4 9 12
#1
DCAS
#1
20 25 28
#1
16 17 18 19
0 1
#2
#2
4 9 12
#2
0 1 2
0 1 2 3
16 17 18
T2: remove 19
GCAS - generation-compare-and-swap
snapshot #1
root
#2
#1
#1
#1
4 9 12
20 25 28
#1
#1
16 17 18 19
0 1
16 17 18
T2: remove 19
prev
#2
#2
4 9 12
#2
0 1 2 3
GCAS - generation-compare-and-swap
snapshot #1
root
#2
#1
#1
#1
4 9 12
#1
20 25 28
#1
2) CAS
16 17 18 19
0 1
16 17 18
T2: remove 19
prev
#2
#2
4 9 12
#2
0 1 2 3
GCAS - generation-compare-and-swap
snapshot #1
root
#2
#1
#1
#1
4 9 12
20 25 28
#1
#1
16 17 18 19
0 1
16 17 18
T2: remove 19
prev
#2
#2
4 9 12
#2
0 1 2 3
GCAS - generation-compare-and-swap
snapshot #1
root
#2
#1
#1
4 9 12
#1
#2
20 25 28
#1
#2
4 9 12
#2
#1
16 17 18 19
0 1
0 1 2 3
16 17 18
GCAS - generation-compare-and-swap
snapshot #1
root
#2
#1
#1
4 9 12
#1
#2
20 25 28
#1
#1
16 17 18 19
0 1
16 17 18 prev
FN
#2
4 9 12
#2
0 1 2 3
GCAS - generation-compare-and-swap
snapshot #1
root
#2
#1
#1
4 9 12
#1
#2
20 25 28
#1
#2
4 9 12
#2
#1
16 17 18 19
0 1
16 17 18 prev
FN
0 1 2 3
GCAS - generation-compare-and-swap
snapshot #1
root
#2
#1
#1
4 9 12
#1
#1
#2
20 25 28
#2
4 9 12
#2
#1
16 17 18 19
0 1
16 17 18
0 1 2 3
GCAS - generation-compare-and-swap
snapshot #1
root
#2
#1
#1
4 9 12
#1
#1
#2
20 25 28
#1
#2
4 9 12
#2
16 17 18 19
0 1
16 17 18
0 1 2 3
GCAS - generation-compare-and-swap
snapshot #1
root
#2
#1
#1
4 9 12
#1
0 1
#1
#2
20 25 28
#2
4 9 12
#2
#1
16 17 18 19
0 1 2 3
Snapshot-based iterator
def iterator =
if (isSnapshot) new Iterator(root)
else snapshot().iterator()
Snapshot-based size
def size = {
val sz = 0
val it = iterator
while (it.hasNext) sz += 1
sz
}
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(logkn)!
(see source code)
(roughly)
Evaluation UltraSPARC T2
Evaluation 4x 8-core i7
Evaluation snapshot
Conclusion
Thank you!