mirror of
https://github.com/talent-plan/tinykv.git
synced 2025-01-15 06:41:35 +08:00
5e089a2cd1
Signed-off-by: Connor <zbk602423539@gmail.com> Co-authored-by: Nick Cameron <nrc@ncameron.org> Co-authored-by: linning <linningde25@gmail.com> Co-authored-by: YangKeao <keao.yang@yahoo.com> Co-authored-by: andylokandy <andylokandy@hotmail.com> Co-authored-by: Iosmanthus Teng <myosmanthustree@gmail.com>
867 lines
20 KiB
Go
867 lines
20 KiB
Go
// Copyright 2014 Google Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// 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 btree
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"math/rand"
|
|
"reflect"
|
|
"sort"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func init() {
|
|
seed := time.Now().Unix()
|
|
fmt.Println(seed)
|
|
rand.Seed(seed)
|
|
}
|
|
|
|
// perm returns a random permutation of n Int items in the range [0, n).
|
|
func perm(n int) (out []Item) {
|
|
for _, v := range rand.Perm(n) {
|
|
out = append(out, Int(v))
|
|
}
|
|
return
|
|
}
|
|
|
|
// rang returns an ordered list of Int items in the range [0, n).
|
|
func rang(n int) (out []Item) {
|
|
for i := 0; i < n; i++ {
|
|
out = append(out, Int(i))
|
|
}
|
|
return
|
|
}
|
|
|
|
// all extracts all items from a tree in order as a slice.
|
|
func all(t *BTree) (out []Item) {
|
|
t.Ascend(func(a Item) bool {
|
|
out = append(out, a)
|
|
return true
|
|
})
|
|
return
|
|
}
|
|
|
|
// rangerev returns a reversed ordered list of Int items in the range [0, n).
|
|
func rangrev(n int) (out []Item) {
|
|
for i := n - 1; i >= 0; i-- {
|
|
out = append(out, Int(i))
|
|
}
|
|
return
|
|
}
|
|
|
|
// allrev extracts all items from a tree in reverse order as a slice.
|
|
func allrev(t *BTree) (out []Item) {
|
|
t.Descend(func(a Item) bool {
|
|
out = append(out, a)
|
|
return true
|
|
})
|
|
return
|
|
}
|
|
|
|
func assertEq(t *testing.T, desc string, got, need interface{}) {
|
|
if !reflect.DeepEqual(need, got) {
|
|
t.Fatalf("%s failed: need %T %v, but got %T %v", desc, need, need, got, got)
|
|
}
|
|
}
|
|
|
|
func TestBTreeSizeInfo(t *testing.T) {
|
|
tr := New(*btreeDegree)
|
|
const maxElt = 10000
|
|
elements := perm(maxElt)
|
|
// insert 0..maxElt
|
|
for _, elt := range elements {
|
|
tr.ReplaceOrInsert(elt)
|
|
assertEq(t, "root length", tr.getRootLength(), tr.Len())
|
|
assertEq(t, "check min", tr.GetAt(0), tr.Min())
|
|
assertEq(t, "check max", tr.GetAt(tr.Len()-1), tr.Max())
|
|
}
|
|
for k := 0; k < maxElt; k++ {
|
|
assertEq(t, "get k-th", tr.GetAt(k), Int(k))
|
|
}
|
|
for x := 0; x < maxElt; x++ {
|
|
y, rk := tr.GetWithIndex(Int(x))
|
|
assertEq(t, "get", y, Int(x))
|
|
assertEq(t, "get rank", rk, x)
|
|
}
|
|
|
|
// get rank of maxElt + 1
|
|
{
|
|
y, rk := tr.GetWithIndex(Int(maxElt + 1))
|
|
assertEq(t, "get max+1", y, nil)
|
|
assertEq(t, "get max+1 rank", rk, tr.Len())
|
|
}
|
|
|
|
// delete x if x % 3 == 0
|
|
for _, elt := range elements {
|
|
if int(elt.(Int))%3 != 0 {
|
|
tr.Delete(elt)
|
|
}
|
|
assertEq(t, "after delete root length", tr.getRootLength(), tr.Len())
|
|
assertEq(t, "after delete check min", tr.GetAt(0), tr.Min())
|
|
assertEq(t, "after delete check max", tr.GetAt(tr.Len()-1), tr.Max())
|
|
}
|
|
for k := 0; k < maxElt/3; k++ {
|
|
assertEq(t, "after delete get k-th", tr.GetAt(k), Int(3*k))
|
|
}
|
|
for x := 0; x < maxElt; x++ {
|
|
y, rk := tr.GetWithIndex(Int(x))
|
|
if x%3 == 0 {
|
|
assertEq(t, "after delete get", y, Int(x))
|
|
assertEq(t, "after delete get rank", rk, x/3)
|
|
} else {
|
|
assertEq(t, "after delete get nil", y, nil)
|
|
assertEq(t, "after delete get nil rank", rk, x/3+1)
|
|
}
|
|
}
|
|
|
|
// delete max until tr.Len() <= 100
|
|
for tr.Len() > 100 {
|
|
tr.DeleteMax()
|
|
assertEq(t, "delete max root length", tr.getRootLength(), tr.Len())
|
|
assertEq(t, "delete max check min", tr.GetAt(0), tr.Min())
|
|
assertEq(t, "delete max check max", tr.GetAt(tr.Len()-1), tr.Max())
|
|
}
|
|
for k := 0; k < maxElt/3 && k < 100; k++ {
|
|
assertEq(t, "delete max get k-th", tr.GetAt(k), Int(3*k))
|
|
}
|
|
for x := 0; x < maxElt && x < 300; x++ {
|
|
y, rk := tr.GetWithIndex(Int(x))
|
|
if x%3 == 0 {
|
|
assertEq(t, "delete max get", y, Int(x))
|
|
assertEq(t, "delete max get rank", rk, x/3)
|
|
} else {
|
|
assertEq(t, "delete max get nil", y, nil)
|
|
assertEq(t, "delete max get nil rank", rk, x/3+1)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
var btreeDegree = flag.Int("degree", 32, "B-Tree degree")
|
|
|
|
func TestBTree(t *testing.T) {
|
|
tr := New(*btreeDegree)
|
|
const treeSize = 10000
|
|
for i := 0; i < 10; i++ {
|
|
if min := tr.Min(); min != nil {
|
|
t.Fatalf("empty min, got %+v", min)
|
|
}
|
|
if max := tr.Max(); max != nil {
|
|
t.Fatalf("empty max, got %+v", max)
|
|
}
|
|
for _, item := range perm(treeSize) {
|
|
if x := tr.ReplaceOrInsert(item); x != nil {
|
|
t.Fatal("insert found item", item)
|
|
}
|
|
}
|
|
for _, item := range perm(treeSize) {
|
|
if x := tr.ReplaceOrInsert(item); x == nil {
|
|
t.Fatal("insert didn't find item", item)
|
|
}
|
|
}
|
|
if min, want := tr.Min(), Item(Int(0)); min != want {
|
|
t.Fatalf("min: want %+v, got %+v", want, min)
|
|
}
|
|
if max, want := tr.Max(), Item(Int(treeSize-1)); max != want {
|
|
t.Fatalf("max: want %+v, got %+v", want, max)
|
|
}
|
|
got := all(tr)
|
|
want := rang(treeSize)
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("mismatch:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
|
|
gotrev := allrev(tr)
|
|
wantrev := rangrev(treeSize)
|
|
if !reflect.DeepEqual(gotrev, wantrev) {
|
|
t.Fatalf("mismatch:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
|
|
for _, item := range perm(treeSize) {
|
|
if x := tr.Delete(item); x == nil {
|
|
t.Fatalf("didn't find %v", item)
|
|
}
|
|
}
|
|
if got = all(tr); len(got) > 0 {
|
|
t.Fatalf("some left!: %v", got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func ExampleBTree() {
|
|
tr := New(*btreeDegree)
|
|
for i := Int(0); i < 10; i++ {
|
|
tr.ReplaceOrInsert(i)
|
|
}
|
|
fmt.Println("len: ", tr.Len())
|
|
fmt.Println("get3: ", tr.Get(Int(3)))
|
|
fmt.Println("get100: ", tr.Get(Int(100)))
|
|
fmt.Println("del4: ", tr.Delete(Int(4)))
|
|
fmt.Println("del100: ", tr.Delete(Int(100)))
|
|
fmt.Println("replace5: ", tr.ReplaceOrInsert(Int(5)))
|
|
fmt.Println("replace100:", tr.ReplaceOrInsert(Int(100)))
|
|
fmt.Println("min: ", tr.Min())
|
|
fmt.Println("delmin: ", tr.DeleteMin())
|
|
fmt.Println("max: ", tr.Max())
|
|
fmt.Println("delmax: ", tr.DeleteMax())
|
|
fmt.Println("len: ", tr.Len())
|
|
// Output:
|
|
// len: 10
|
|
// get3: 3
|
|
// get100: <nil>
|
|
// del4: 4
|
|
// del100: <nil>
|
|
// replace5: 5
|
|
// replace100: <nil>
|
|
// min: 0
|
|
// delmin: 0
|
|
// max: 100
|
|
// delmax: 100
|
|
// len: 8
|
|
}
|
|
|
|
func TestDeleteMin(t *testing.T) {
|
|
tr := New(3)
|
|
for _, v := range perm(100) {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
var got []Item
|
|
for v := tr.DeleteMin(); v != nil; v = tr.DeleteMin() {
|
|
got = append(got, v)
|
|
}
|
|
if want := rang(100); !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestDeleteMax(t *testing.T) {
|
|
tr := New(3)
|
|
for _, v := range perm(100) {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
var got []Item
|
|
for v := tr.DeleteMax(); v != nil; v = tr.DeleteMax() {
|
|
got = append(got, v)
|
|
}
|
|
// Reverse our list.
|
|
for i := 0; i < len(got)/2; i++ {
|
|
got[i], got[len(got)-i-1] = got[len(got)-i-1], got[i]
|
|
}
|
|
if want := rang(100); !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestAscendRange(t *testing.T) {
|
|
tr := New(2)
|
|
for _, v := range perm(100) {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
var got []Item
|
|
tr.AscendRange(Int(40), Int(60), func(a Item) bool {
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rang(100)[40:60]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
got = got[:0]
|
|
tr.AscendRange(Int(40), Int(60), func(a Item) bool {
|
|
if a.(Int) > 50 {
|
|
return false
|
|
}
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rang(100)[40:51]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestDescendRange(t *testing.T) {
|
|
tr := New(2)
|
|
for _, v := range perm(100) {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
var got []Item
|
|
tr.DescendRange(Int(60), Int(40), func(a Item) bool {
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rangrev(100)[39:59]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("descendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
got = got[:0]
|
|
tr.DescendRange(Int(60), Int(40), func(a Item) bool {
|
|
if a.(Int) < 50 {
|
|
return false
|
|
}
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rangrev(100)[39:50]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("descendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
func TestAscendLessThan(t *testing.T) {
|
|
tr := New(*btreeDegree)
|
|
for _, v := range perm(100) {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
var got []Item
|
|
tr.AscendLessThan(Int(60), func(a Item) bool {
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rang(100)[:60]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
got = got[:0]
|
|
tr.AscendLessThan(Int(60), func(a Item) bool {
|
|
if a.(Int) > 50 {
|
|
return false
|
|
}
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rang(100)[:51]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestDescendLessOrEqual(t *testing.T) {
|
|
tr := New(*btreeDegree)
|
|
for _, v := range perm(100) {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
var got []Item
|
|
tr.DescendLessOrEqual(Int(40), func(a Item) bool {
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rangrev(100)[59:]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("descendlessorequal:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
got = got[:0]
|
|
tr.DescendLessOrEqual(Int(60), func(a Item) bool {
|
|
if a.(Int) < 50 {
|
|
return false
|
|
}
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rangrev(100)[39:50]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("descendlessorequal:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
func TestAscendGreaterOrEqual(t *testing.T) {
|
|
tr := New(*btreeDegree)
|
|
for _, v := range perm(100) {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
var got []Item
|
|
tr.AscendGreaterOrEqual(Int(40), func(a Item) bool {
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rang(100)[40:]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
got = got[:0]
|
|
tr.AscendGreaterOrEqual(Int(40), func(a Item) bool {
|
|
if a.(Int) > 50 {
|
|
return false
|
|
}
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rang(100)[40:51]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestDescendGreaterThan(t *testing.T) {
|
|
tr := New(*btreeDegree)
|
|
for _, v := range perm(100) {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
var got []Item
|
|
tr.DescendGreaterThan(Int(40), func(a Item) bool {
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rangrev(100)[:59]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("descendgreaterthan:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
got = got[:0]
|
|
tr.DescendGreaterThan(Int(40), func(a Item) bool {
|
|
if a.(Int) < 50 {
|
|
return false
|
|
}
|
|
got = append(got, a)
|
|
return true
|
|
})
|
|
if want := rangrev(100)[:50]; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("descendgreaterthan:\n got: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
const benchmarkTreeSize = 10000
|
|
|
|
func BenchmarkInsert(b *testing.B) {
|
|
b.StopTimer()
|
|
insertP := perm(benchmarkTreeSize)
|
|
b.StartTimer()
|
|
i := 0
|
|
for i < b.N {
|
|
tr := New(*btreeDegree)
|
|
for _, item := range insertP {
|
|
tr.ReplaceOrInsert(item)
|
|
i++
|
|
if i >= b.N {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSeek(b *testing.B) {
|
|
b.StopTimer()
|
|
size := 100000
|
|
insertP := perm(size)
|
|
tr := New(*btreeDegree)
|
|
for _, item := range insertP {
|
|
tr.ReplaceOrInsert(item)
|
|
}
|
|
b.StartTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
tr.AscendGreaterOrEqual(Int(i%size), func(i Item) bool { return false })
|
|
}
|
|
}
|
|
|
|
func BenchmarkDeleteInsert(b *testing.B) {
|
|
b.StopTimer()
|
|
insertP := perm(benchmarkTreeSize)
|
|
tr := New(*btreeDegree)
|
|
for _, item := range insertP {
|
|
tr.ReplaceOrInsert(item)
|
|
}
|
|
b.StartTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
tr.Delete(insertP[i%benchmarkTreeSize])
|
|
tr.ReplaceOrInsert(insertP[i%benchmarkTreeSize])
|
|
}
|
|
}
|
|
|
|
func BenchmarkDeleteInsertCloneOnce(b *testing.B) {
|
|
b.StopTimer()
|
|
insertP := perm(benchmarkTreeSize)
|
|
tr := New(*btreeDegree)
|
|
for _, item := range insertP {
|
|
tr.ReplaceOrInsert(item)
|
|
}
|
|
tr = tr.Clone()
|
|
b.StartTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
tr.Delete(insertP[i%benchmarkTreeSize])
|
|
tr.ReplaceOrInsert(insertP[i%benchmarkTreeSize])
|
|
}
|
|
}
|
|
|
|
func BenchmarkDeleteInsertCloneEachTime(b *testing.B) {
|
|
b.StopTimer()
|
|
insertP := perm(benchmarkTreeSize)
|
|
tr := New(*btreeDegree)
|
|
for _, item := range insertP {
|
|
tr.ReplaceOrInsert(item)
|
|
}
|
|
b.StartTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
tr = tr.Clone()
|
|
tr.Delete(insertP[i%benchmarkTreeSize])
|
|
tr.ReplaceOrInsert(insertP[i%benchmarkTreeSize])
|
|
}
|
|
}
|
|
|
|
func BenchmarkDelete(b *testing.B) {
|
|
b.StopTimer()
|
|
insertP := perm(benchmarkTreeSize)
|
|
removeP := perm(benchmarkTreeSize)
|
|
b.StartTimer()
|
|
i := 0
|
|
for i < b.N {
|
|
b.StopTimer()
|
|
tr := New(*btreeDegree)
|
|
for _, v := range insertP {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
b.StartTimer()
|
|
for _, item := range removeP {
|
|
tr.Delete(item)
|
|
i++
|
|
if i >= b.N {
|
|
return
|
|
}
|
|
}
|
|
if tr.Len() > 0 {
|
|
panic(tr.Len())
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkGet(b *testing.B) {
|
|
b.StopTimer()
|
|
insertP := perm(benchmarkTreeSize)
|
|
removeP := perm(benchmarkTreeSize)
|
|
b.StartTimer()
|
|
i := 0
|
|
for i < b.N {
|
|
b.StopTimer()
|
|
tr := New(*btreeDegree)
|
|
for _, v := range insertP {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
b.StartTimer()
|
|
for _, item := range removeP {
|
|
tr.Get(item)
|
|
i++
|
|
if i >= b.N {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkGetCloneEachTime(b *testing.B) {
|
|
b.StopTimer()
|
|
insertP := perm(benchmarkTreeSize)
|
|
removeP := perm(benchmarkTreeSize)
|
|
b.StartTimer()
|
|
i := 0
|
|
for i < b.N {
|
|
b.StopTimer()
|
|
tr := New(*btreeDegree)
|
|
for _, v := range insertP {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
b.StartTimer()
|
|
for _, item := range removeP {
|
|
tr = tr.Clone()
|
|
tr.Get(item)
|
|
i++
|
|
if i >= b.N {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type byInts []Item
|
|
|
|
func (a byInts) Len() int {
|
|
return len(a)
|
|
}
|
|
|
|
func (a byInts) Less(i, j int) bool {
|
|
return a[i].(Int) < a[j].(Int)
|
|
}
|
|
|
|
func (a byInts) Swap(i, j int) {
|
|
a[i], a[j] = a[j], a[i]
|
|
}
|
|
|
|
func BenchmarkAscend(b *testing.B) {
|
|
arr := perm(benchmarkTreeSize)
|
|
tr := New(*btreeDegree)
|
|
for _, v := range arr {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
sort.Sort(byInts(arr))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
j := 0
|
|
tr.Ascend(func(item Item) bool {
|
|
if item.(Int) != arr[j].(Int) {
|
|
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
|
|
}
|
|
j++
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkDescend(b *testing.B) {
|
|
arr := perm(benchmarkTreeSize)
|
|
tr := New(*btreeDegree)
|
|
for _, v := range arr {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
sort.Sort(byInts(arr))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
j := len(arr) - 1
|
|
tr.Descend(func(item Item) bool {
|
|
if item.(Int) != arr[j].(Int) {
|
|
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
|
|
}
|
|
j--
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
func BenchmarkAscendRange(b *testing.B) {
|
|
arr := perm(benchmarkTreeSize)
|
|
tr := New(*btreeDegree)
|
|
for _, v := range arr {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
sort.Sort(byInts(arr))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
j := 100
|
|
tr.AscendRange(Int(100), arr[len(arr)-100], func(item Item) bool {
|
|
if item.(Int) != arr[j].(Int) {
|
|
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
|
|
}
|
|
j++
|
|
return true
|
|
})
|
|
if j != len(arr)-100 {
|
|
b.Fatalf("expected: %v, got %v", len(arr)-100, j)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkDescendRange(b *testing.B) {
|
|
arr := perm(benchmarkTreeSize)
|
|
tr := New(*btreeDegree)
|
|
for _, v := range arr {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
sort.Sort(byInts(arr))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
j := len(arr) - 100
|
|
tr.DescendRange(arr[len(arr)-100], Int(100), func(item Item) bool {
|
|
if item.(Int) != arr[j].(Int) {
|
|
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
|
|
}
|
|
j--
|
|
return true
|
|
})
|
|
if j != 100 {
|
|
b.Fatalf("expected: %v, got %v", len(arr)-100, j)
|
|
}
|
|
}
|
|
}
|
|
func BenchmarkAscendGreaterOrEqual(b *testing.B) {
|
|
arr := perm(benchmarkTreeSize)
|
|
tr := New(*btreeDegree)
|
|
for _, v := range arr {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
sort.Sort(byInts(arr))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
j := 100
|
|
k := 0
|
|
tr.AscendGreaterOrEqual(Int(100), func(item Item) bool {
|
|
if item.(Int) != arr[j].(Int) {
|
|
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
|
|
}
|
|
j++
|
|
k++
|
|
return true
|
|
})
|
|
if j != len(arr) {
|
|
b.Fatalf("expected: %v, got %v", len(arr), j)
|
|
}
|
|
if k != len(arr)-100 {
|
|
b.Fatalf("expected: %v, got %v", len(arr)-100, k)
|
|
}
|
|
}
|
|
}
|
|
func BenchmarkDescendLessOrEqual(b *testing.B) {
|
|
arr := perm(benchmarkTreeSize)
|
|
tr := New(*btreeDegree)
|
|
for _, v := range arr {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
sort.Sort(byInts(arr))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
j := len(arr) - 100
|
|
k := len(arr)
|
|
tr.DescendLessOrEqual(arr[len(arr)-100], func(item Item) bool {
|
|
if item.(Int) != arr[j].(Int) {
|
|
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
|
|
}
|
|
j--
|
|
k--
|
|
return true
|
|
})
|
|
if j != -1 {
|
|
b.Fatalf("expected: %v, got %v", -1, j)
|
|
}
|
|
if k != 99 {
|
|
b.Fatalf("expected: %v, got %v", 99, k)
|
|
}
|
|
}
|
|
}
|
|
|
|
const cloneTestSize = 10000
|
|
|
|
func cloneTest(t *testing.T, b *BTree, start int, p []Item, wg *sync.WaitGroup, trees *[]*BTree, lock *sync.Mutex) {
|
|
t.Logf("Starting new clone at %v", start)
|
|
lock.Lock()
|
|
*trees = append(*trees, b)
|
|
lock.Unlock()
|
|
for i := start; i < cloneTestSize; i++ {
|
|
b.ReplaceOrInsert(p[i])
|
|
if i%(cloneTestSize/5) == 0 {
|
|
wg.Add(1)
|
|
go cloneTest(t, b.Clone(), i+1, p, wg, trees, lock)
|
|
}
|
|
}
|
|
wg.Done()
|
|
}
|
|
|
|
func TestCloneConcurrentOperations(t *testing.T) {
|
|
b := New(*btreeDegree)
|
|
var trees []*BTree
|
|
p := perm(cloneTestSize)
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
go cloneTest(t, b, 0, p, &wg, &trees, &sync.Mutex{})
|
|
wg.Wait()
|
|
want := rang(cloneTestSize)
|
|
t.Logf("Starting equality checks on %d trees", len(trees))
|
|
for i, tree := range trees {
|
|
if !reflect.DeepEqual(want, all(tree)) {
|
|
t.Errorf("tree %v mismatch", i)
|
|
}
|
|
}
|
|
t.Log("Removing half from first half")
|
|
toRemove := rang(cloneTestSize)[cloneTestSize/2:]
|
|
for i := 0; i < len(trees)/2; i++ {
|
|
tree := trees[i]
|
|
wg.Add(1)
|
|
go func() {
|
|
for _, item := range toRemove {
|
|
tree.Delete(item)
|
|
}
|
|
wg.Done()
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
t.Log("Checking all values again")
|
|
for i, tree := range trees {
|
|
var wantpart []Item
|
|
if i < len(trees)/2 {
|
|
wantpart = want[:cloneTestSize/2]
|
|
} else {
|
|
wantpart = want
|
|
}
|
|
if got := all(tree); !reflect.DeepEqual(wantpart, got) {
|
|
t.Errorf("tree %v mismatch, want %v got %v", i, len(want), len(got))
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkDeleteAndRestore(b *testing.B) {
|
|
items := perm(16392)
|
|
b.ResetTimer()
|
|
b.Run(`CopyBigFreeList`, func(b *testing.B) {
|
|
fl := NewFreeList(16392)
|
|
tr := NewWithFreeList(*btreeDegree, fl)
|
|
for _, v := range items {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
dels := make([]Item, 0, tr.Len())
|
|
tr.Ascend(ItemIterator(func(b Item) bool {
|
|
dels = append(dels, b)
|
|
return true
|
|
}))
|
|
for _, del := range dels {
|
|
tr.Delete(del)
|
|
}
|
|
// tr is now empty, we make a new empty copy of it.
|
|
tr = NewWithFreeList(*btreeDegree, fl)
|
|
for _, v := range items {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
}
|
|
})
|
|
b.Run(`Copy`, func(b *testing.B) {
|
|
tr := New(*btreeDegree)
|
|
for _, v := range items {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
dels := make([]Item, 0, tr.Len())
|
|
tr.Ascend(ItemIterator(func(b Item) bool {
|
|
dels = append(dels, b)
|
|
return true
|
|
}))
|
|
for _, del := range dels {
|
|
tr.Delete(del)
|
|
}
|
|
// tr is now empty, we make a new empty copy of it.
|
|
tr = New(*btreeDegree)
|
|
for _, v := range items {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
}
|
|
})
|
|
b.Run(`ClearBigFreelist`, func(b *testing.B) {
|
|
fl := NewFreeList(16392)
|
|
tr := NewWithFreeList(*btreeDegree, fl)
|
|
for _, v := range items {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
tr.Clear(true)
|
|
for _, v := range items {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
}
|
|
})
|
|
b.Run(`Clear`, func(b *testing.B) {
|
|
tr := New(*btreeDegree)
|
|
for _, v := range items {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
tr.Clear(true)
|
|
for _, v := range items {
|
|
tr.ReplaceOrInsert(v)
|
|
}
|
|
}
|
|
})
|
|
}
|