Skip to content

Commit 959ef5b

Browse files
Merge pull request #57 from Workiva/btrees_with_skiplists
Btrees with skiplists
2 parents 8ff933b + fe62d5c commit 959ef5b

File tree

9 files changed

+229
-121
lines changed

9 files changed

+229
-121
lines changed

btree/palm/interface.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,20 @@ for in-memory indices. Otherwise, the operations have typical B-tree
2626
time complexities.
2727
2828
You primarily see the benefits of multithreading in availability and
29-
bulk operations, below is a benchmark against the B-plus tree in this
30-
package.
29+
bulk operations.
3130
32-
BenchmarkBulkAddToExisting-8 200 8690207 ns/op
33-
BenchmarkBulkAddToExisting-8 100 16778514 ns/op
31+
Benchmarks:
32+
33+
BenchmarkReadAndWrites-8 1000 1543648 ns/op
34+
BenchmarkBulkAdd-8 1000 1705673 ns/op
35+
BenchmarkBulkAddToExisting-8 100 70056512 ns/op
36+
BenchmarkGet-8 100000 17128 ns/op
37+
BenchmarkBulkGet-8 3000 507249 ns/op
3438
*/
3539
package palm
3640

41+
import "github.com/Workiva/go-datastructures/slice/skip"
42+
3743
// Keys is a typed list of Key interfaces.
3844
type Keys []Key
3945

@@ -44,7 +50,7 @@ type Key interface {
4450
// to the provided key. -1 will indicate less than, 0 will indicate
4551
// equality, and 1 will indicate greater than. Duplicate keys
4652
// are allowed, but duplicate IDs are not.
47-
Compare(Key) int
53+
Compare(skip.Entry) int
4854
}
4955

5056
// BTree is the interface returned from this package's constructor.

btree/palm/key.go

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,48 +16,6 @@ limitations under the License.
1616

1717
package palm
1818

19-
import "sort"
20-
21-
func (keys Keys) search(key Key) int {
22-
return sort.Search(len(keys), func(i int) bool {
23-
return keys[i].Compare(key) > -1
24-
})
25-
}
26-
27-
func (keys *Keys) insert(key Key) Key {
28-
i := keys.search(key)
29-
return keys.insertAt(key, i)
30-
}
31-
32-
func (keys *Keys) insertAt(key Key, i int) Key {
33-
if i == len(*keys) {
34-
*keys = append(*keys, key)
35-
return nil
36-
}
37-
38-
if (*keys)[i].Compare(key) == 0 { //overwrite case
39-
oldKey := (*keys)[i]
40-
(*keys)[i] = key
41-
return oldKey
42-
}
43-
44-
*keys = append(*keys, nil)
45-
copy((*keys)[i+1:], (*keys)[i:])
46-
(*keys)[i] = key
47-
return nil
48-
}
49-
50-
func (keys *Keys) splitAt(i int) (Keys, Keys) {
51-
right := make(Keys, len(*keys)-i-1, cap(*keys))
52-
copy(right, (*keys)[i+1:])
53-
for j := i + 1; j < len(*keys); j++ {
54-
(*keys)[j] = nil
55-
}
56-
*keys = (*keys)[:i+1]
57-
58-
return *keys, right
59-
}
60-
6119
func (keys Keys) reverse() Keys {
6220
reversed := make(Keys, len(keys))
6321
for i := len(keys) - 1; i >= 0; i-- {

btree/palm/mock_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
16-
1716
package palm
1817

18+
import "github.com/Workiva/go-datastructures/slice/skip"
19+
1920
type mockKey int
2021

21-
func (mk mockKey) Compare(other Key) int {
22+
func (mk mockKey) Compare(other skip.Entry) int {
2223
otherKey := other.(mockKey)
2324

2425
if mk == otherKey {

btree/palm/node.go

Lines changed: 126 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ limitations under the License.
1616

1717
package palm
1818

19-
import "log"
19+
import (
20+
"log"
21+
22+
"github.com/Workiva/go-datastructures/slice/skip"
23+
)
2024

2125
func getParent(parent *node, key Key) *node {
2226
var n *node
@@ -28,72 +32,142 @@ func getParent(parent *node, key Key) *node {
2832
return parent
2933
}
3034

31-
type nodes []*node
35+
type nodes struct {
36+
list *skip.SkipList
37+
}
3238

3339
func (ns *nodes) push(n *node) {
34-
*ns = append(*ns, n)
40+
ns.list.InsertAtPosition(ns.list.Len(), n)
41+
}
42+
43+
func (ns *nodes) splitAt(i uint64) (*nodes, *nodes) {
44+
_, right := ns.list.SplitAt(i)
45+
return ns, &nodes{list: right}
3546
}
3647

37-
func (ns *nodes) insertAt(n *node, i int) {
38-
if i == len(*ns) {
39-
*ns = append(*ns, n)
40-
return
48+
func (ns *nodes) byPosition(pos uint64) *node {
49+
n, ok := ns.list.ByPosition(pos).(*node)
50+
if !ok {
51+
return nil
4152
}
4253

43-
*ns = append(*ns, nil)
44-
copy((*ns)[i+1:], (*ns)[i:])
45-
(*ns)[i] = n
54+
return n
55+
}
56+
57+
func (ns *nodes) insertAt(i uint64, n *node) {
58+
ns.list.InsertAtPosition(i, n)
59+
}
60+
61+
func (ns *nodes) replaceAt(i uint64, n *node) {
62+
ns.list.ReplaceAtPosition(i, n)
63+
}
64+
65+
func (ns *nodes) len() uint64 {
66+
return ns.list.Len()
67+
}
68+
69+
func newNodes() *nodes {
70+
return &nodes{
71+
list: skip.New(uint64(0)),
72+
}
73+
}
74+
75+
type keys struct {
76+
list *skip.SkipList
77+
}
78+
79+
func (ks *keys) splitAt(i uint64) (*keys, *keys) {
80+
_, right := ks.list.SplitAt(i)
81+
return ks, &keys{list: right}
82+
}
83+
84+
func (ks *keys) len() uint64 {
85+
return ks.list.Len()
86+
}
87+
88+
func (ks *keys) byPosition(i uint64) Key {
89+
k, ok := ks.list.ByPosition(i).(Key)
90+
if !ok {
91+
return nil
92+
}
93+
94+
return k
95+
}
96+
97+
func (ks *keys) delete(k Key) {
98+
ks.list.Delete(k.(skip.Entry))
99+
}
100+
101+
func (ks *keys) search(key Key) uint64 {
102+
n, i := ks.list.GetWithPosition(key.(skip.Entry))
103+
if n == nil {
104+
return ks.list.Len()
105+
}
106+
107+
return i
108+
}
109+
110+
func (ks *keys) insert(key Key) Key {
111+
old := ks.list.Insert(key)[0]
112+
if old == nil {
113+
return nil
114+
}
115+
116+
return old.(Key)
117+
}
118+
119+
func (ks *keys) last() Key {
120+
return ks.list.ByPosition(ks.list.Len() - 1).(Key)
121+
}
122+
123+
func (ks *keys) insertAt(i uint64, k Key) {
124+
ks.list.InsertAtPosition(i, k.(skip.Entry))
125+
}
126+
127+
func newKeys() *keys {
128+
return &keys{
129+
list: skip.New(uint64(0)),
130+
}
46131
}
47132

48133
type node struct {
49-
keys Keys
50-
nodes nodes
134+
keys *keys
135+
nodes *nodes
51136
isLeaf bool
52137
parent, right *node
53138
}
54139

55140
func (n *node) needsSplit(ary uint64) bool {
56-
return uint64(len(n.keys)) >= ary
141+
return n.keys.len() >= ary
57142
}
58143

59144
func (n *node) splitLeaf() (Key, *node, *node) {
60-
i := (len(n.keys) / 2)
61-
key := n.keys[i]
145+
i := n.keys.len() / 2
146+
key := n.keys.byPosition(i)
62147
_, rightKeys := n.keys.splitAt(i)
63148
nn := &node{
64149
keys: rightKeys,
150+
nodes: newNodes(),
65151
isLeaf: true,
66152
}
67153
n.right = nn
68154
return key, n, nn
69155
}
70156

71157
func (n *node) splitInternal() (Key, *node, *node) {
72-
i := (len(n.keys) / 2)
73-
key := n.keys[i]
158+
i := n.keys.len() / 2
159+
key := n.keys.byPosition(i)
160+
n.keys.delete(key)
74161

75-
rightKeys := make(Keys, len(n.keys)-1-i, cap(n.keys))
76-
rightNodes := make(nodes, len(rightKeys)+1, cap(n.nodes))
77-
78-
copy(rightKeys, n.keys[i+1:])
79-
copy(rightNodes, n.nodes[i+1:])
80-
81-
// for garbage collection
82-
for j := i + 1; j < len(n.nodes); j++ {
83-
if j != len(n.keys) {
84-
n.keys[j] = nil
85-
}
86-
n.nodes[j] = nil
87-
}
162+
_, rightKeys := n.keys.splitAt(i - 1)
163+
_, rightNodes := n.nodes.splitAt(i)
88164

89165
nn := newNode(false, rightKeys, rightNodes)
90-
for _, nd := range rightNodes {
166+
for iter := rightNodes.list.IterAtPosition(0); iter.Next(); {
167+
nd := iter.Value().(*node)
91168
nd.parent = nn
92169
}
93170

94-
n.keys = n.keys[:i]
95-
n.nodes = n.nodes[:i+1]
96-
97171
return key, n, nn
98172
}
99173

@@ -105,34 +179,47 @@ func (n *node) split() (Key, *node, *node) {
105179
return n.splitInternal()
106180
}
107181

108-
func (n *node) search(key Key) int {
182+
func (n *node) search(key Key) uint64 {
109183
return n.keys.search(key)
110184
}
111185

112186
func (n *node) searchNode(key Key) *node {
113187
i := n.search(key)
114188

115-
return n.nodes[i]
189+
return n.nodes.byPosition(uint64(i))
116190
}
117191

118192
func (n *node) key() Key {
119-
return n.keys[len(n.keys)-1]
193+
return n.keys.last()
120194
}
121195

122196
func (n *node) print(output *log.Logger) {
123197
output.Printf(`NODE: %+v, %p`, n, n)
198+
for iter := n.keys.list.IterAtPosition(0); iter.Next(); {
199+
k := iter.Value().(Key)
200+
output.Printf(`KEY: %+v`, k)
201+
}
124202
if !n.isLeaf {
125-
for _, n := range n.nodes {
203+
for iter := n.nodes.list.IterAtPosition(0); iter.Next(); {
204+
n := iter.Value().(*node)
126205
if n == nil {
127206
output.Println(`NIL NODE`)
128207
continue
129208
}
209+
130210
n.print(output)
131211
}
132212
}
133213
}
134214

135-
func newNode(isLeaf bool, keys Keys, ns nodes) *node {
215+
// Compare is required by the skip.Entry interface but nodes are always
216+
// added by position so while this method is required it doesn't
217+
// need to return anything useful.
218+
func (n *node) Compare(e skip.Entry) int {
219+
return 0
220+
}
221+
222+
func newNode(isLeaf bool, keys *keys, ns *nodes) *node {
136223
return &node{
137224
isLeaf: isLeaf,
138225
keys: keys,

0 commit comments

Comments
 (0)