Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions btree/palm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,20 @@ for in-memory indices. Otherwise, the operations have typical B-tree
time complexities.
You primarily see the benefits of multithreading in availability and
bulk operations, below is a benchmark against the B-plus tree in this
package.
bulk operations.
BenchmarkBulkAddToExisting-8 200 8690207 ns/op
BenchmarkBulkAddToExisting-8 100 16778514 ns/op
Benchmarks:
BenchmarkReadAndWrites-8 1000 1543648 ns/op
BenchmarkBulkAdd-8 1000 1705673 ns/op
BenchmarkBulkAddToExisting-8 100 70056512 ns/op
BenchmarkGet-8 100000 17128 ns/op
BenchmarkBulkGet-8 3000 507249 ns/op
*/
package palm

import "github.com/Workiva/go-datastructures/slice/skip"

// Keys is a typed list of Key interfaces.
type Keys []Key

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

// BTree is the interface returned from this package's constructor.
Expand Down
42 changes: 0 additions & 42 deletions btree/palm/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,48 +16,6 @@ limitations under the License.

package palm

import "sort"

func (keys Keys) search(key Key) int {
return sort.Search(len(keys), func(i int) bool {
return keys[i].Compare(key) > -1
})
}

func (keys *Keys) insert(key Key) Key {
i := keys.search(key)
return keys.insertAt(key, i)
}

func (keys *Keys) insertAt(key Key, i int) Key {
if i == len(*keys) {
*keys = append(*keys, key)
return nil
}

if (*keys)[i].Compare(key) == 0 { //overwrite case
oldKey := (*keys)[i]
(*keys)[i] = key
return oldKey
}

*keys = append(*keys, nil)
copy((*keys)[i+1:], (*keys)[i:])
(*keys)[i] = key
return nil
}

func (keys *Keys) splitAt(i int) (Keys, Keys) {
right := make(Keys, len(*keys)-i-1, cap(*keys))
copy(right, (*keys)[i+1:])
for j := i + 1; j < len(*keys); j++ {
(*keys)[j] = nil
}
*keys = (*keys)[:i+1]

return *keys, right
}

func (keys Keys) reverse() Keys {
reversed := make(Keys, len(keys))
for i := len(keys) - 1; i >= 0; i-- {
Expand Down
5 changes: 3 additions & 2 deletions btree/palm/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package palm

import "github.com/Workiva/go-datastructures/slice/skip"

type mockKey int

func (mk mockKey) Compare(other Key) int {
func (mk mockKey) Compare(other skip.Entry) int {
otherKey := other.(mockKey)

if mk == otherKey {
Expand Down
165 changes: 126 additions & 39 deletions btree/palm/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ limitations under the License.

package palm

import "log"
import (
"log"

"github.com/Workiva/go-datastructures/slice/skip"
)

func getParent(parent *node, key Key) *node {
var n *node
Expand All @@ -28,72 +32,142 @@ func getParent(parent *node, key Key) *node {
return parent
}

type nodes []*node
type nodes struct {
list *skip.SkipList
}

func (ns *nodes) push(n *node) {
*ns = append(*ns, n)
ns.list.InsertAtPosition(ns.list.Len(), n)
}

func (ns *nodes) splitAt(i uint64) (*nodes, *nodes) {
_, right := ns.list.SplitAt(i)
return ns, &nodes{list: right}
}

func (ns *nodes) insertAt(n *node, i int) {
if i == len(*ns) {
*ns = append(*ns, n)
return
func (ns *nodes) byPosition(pos uint64) *node {
n, ok := ns.list.ByPosition(pos).(*node)
if !ok {
return nil
}

*ns = append(*ns, nil)
copy((*ns)[i+1:], (*ns)[i:])
(*ns)[i] = n
return n
}

func (ns *nodes) insertAt(i uint64, n *node) {
ns.list.InsertAtPosition(i, n)
}

func (ns *nodes) replaceAt(i uint64, n *node) {
ns.list.ReplaceAtPosition(i, n)
}

func (ns *nodes) len() uint64 {
return ns.list.Len()
}

func newNodes() *nodes {
return &nodes{
list: skip.New(uint64(0)),
}
}

type keys struct {
list *skip.SkipList
}

func (ks *keys) splitAt(i uint64) (*keys, *keys) {
_, right := ks.list.SplitAt(i)
return ks, &keys{list: right}
}

func (ks *keys) len() uint64 {
return ks.list.Len()
}

func (ks *keys) byPosition(i uint64) Key {
k, ok := ks.list.ByPosition(i).(Key)
if !ok {
return nil
}

return k
}

func (ks *keys) delete(k Key) {
ks.list.Delete(k.(skip.Entry))
}

func (ks *keys) search(key Key) uint64 {
n, i := ks.list.GetWithPosition(key.(skip.Entry))
if n == nil {
return ks.list.Len()
}

return i
}

func (ks *keys) insert(key Key) Key {
old := ks.list.Insert(key)[0]
if old == nil {
return nil
}

return old.(Key)
}

func (ks *keys) last() Key {
return ks.list.ByPosition(ks.list.Len() - 1).(Key)
}

func (ks *keys) insertAt(i uint64, k Key) {
ks.list.InsertAtPosition(i, k.(skip.Entry))
}

func newKeys() *keys {
return &keys{
list: skip.New(uint64(0)),
}
}

type node struct {
keys Keys
nodes nodes
keys *keys
nodes *nodes
isLeaf bool
parent, right *node
}

func (n *node) needsSplit(ary uint64) bool {
return uint64(len(n.keys)) >= ary
return n.keys.len() >= ary
}

func (n *node) splitLeaf() (Key, *node, *node) {
i := (len(n.keys) / 2)
key := n.keys[i]
i := n.keys.len() / 2
key := n.keys.byPosition(i)
_, rightKeys := n.keys.splitAt(i)
nn := &node{
keys: rightKeys,
nodes: newNodes(),
isLeaf: true,
}
n.right = nn
return key, n, nn
}

func (n *node) splitInternal() (Key, *node, *node) {
i := (len(n.keys) / 2)
key := n.keys[i]
i := n.keys.len() / 2
key := n.keys.byPosition(i)
n.keys.delete(key)

rightKeys := make(Keys, len(n.keys)-1-i, cap(n.keys))
rightNodes := make(nodes, len(rightKeys)+1, cap(n.nodes))

copy(rightKeys, n.keys[i+1:])
copy(rightNodes, n.nodes[i+1:])

// for garbage collection
for j := i + 1; j < len(n.nodes); j++ {
if j != len(n.keys) {
n.keys[j] = nil
}
n.nodes[j] = nil
}
_, rightKeys := n.keys.splitAt(i - 1)
_, rightNodes := n.nodes.splitAt(i)

nn := newNode(false, rightKeys, rightNodes)
for _, nd := range rightNodes {
for iter := rightNodes.list.IterAtPosition(0); iter.Next(); {
nd := iter.Value().(*node)
nd.parent = nn
}

n.keys = n.keys[:i]
n.nodes = n.nodes[:i+1]

return key, n, nn
}

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

func (n *node) search(key Key) int {
func (n *node) search(key Key) uint64 {
return n.keys.search(key)
}

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

return n.nodes[i]
return n.nodes.byPosition(uint64(i))
}

func (n *node) key() Key {
return n.keys[len(n.keys)-1]
return n.keys.last()
}

func (n *node) print(output *log.Logger) {
output.Printf(`NODE: %+v, %p`, n, n)
for iter := n.keys.list.IterAtPosition(0); iter.Next(); {
k := iter.Value().(Key)
output.Printf(`KEY: %+v`, k)
}
if !n.isLeaf {
for _, n := range n.nodes {
for iter := n.nodes.list.IterAtPosition(0); iter.Next(); {
n := iter.Value().(*node)
if n == nil {
output.Println(`NIL NODE`)
continue
}

n.print(output)
}
}
}

func newNode(isLeaf bool, keys Keys, ns nodes) *node {
// Compare is required by the skip.Entry interface but nodes are always
// added by position so while this method is required it doesn't
// need to return anything useful.
func (n *node) Compare(e skip.Entry) int {
return 0
}

func newNode(isLeaf bool, keys *keys, ns *nodes) *node {
return &node{
isLeaf: isLeaf,
keys: keys,
Expand Down
Loading