Commit 5aafa674 authored by Jan Mercl's avatar Jan Mercl

Lower GC presure by recycling things.

parent d2d7ab5c
.PHONY: all todo clean cover generic
.PHONY: all todo clean cover generic mem nuke
testbin=b.test
all: editor
go build
......@@ -11,12 +13,6 @@ editor:
go test -i
go test
todo:
@grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* *.go || true
@grep -n TODO *.go || true
@grep -n BUG *.go || true
@grep -n println *.go || true
clean:
@go clean
rm -f *~
......@@ -31,3 +27,17 @@ generic:
@# Intended use is to replace all textual occurrences of KEY or VALUE in
@# the output with your desired types.
@sed -e 's|interface{}[^{]*/\*K\*/|KEY|g' -e 's|interface{}[^{]*/\*V\*/|VALUE|g' btree.go
mem:
go test -c
./$(testbin) -test.bench . -test.memprofile mem.out -test.memprofilerate 1
go tool pprof --lines --web --alloc_space $(testbin) mem.out
nuke: clean
rm -f *.test *.out
todo:
@grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* *.go || true
@grep -n TODO *.go || true
@grep -n BUG *.go || true
@grep -n println *.go || true
// Copyright 2013 The Go Authors. All rights reserved.
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
......@@ -279,6 +279,8 @@ func benchmarkSetSeq(b *testing.B, n int) {
for j := 0; j < n; j++ {
r.Set(j, j)
}
b.StopTimer()
r.Close()
}
b.StopTimer()
}
......@@ -312,6 +314,7 @@ func benchmarkGetSeq(b *testing.B, n int) {
}
}
b.StopTimer()
r.Close()
}
func BenchmarkSetRnd1e3(b *testing.B) {
......@@ -345,6 +348,8 @@ func benchmarkSetRnd(b *testing.B, n int) {
for _, v := range a {
r.Set(v, 0)
}
b.StopTimer()
r.Close()
}
b.StopTimer()
}
......@@ -383,6 +388,7 @@ func benchmarkGetRnd(b *testing.B, n int) {
}
}
b.StopTimer()
r.Close()
}
func TestSetGet2(t *testing.T) {
......@@ -557,19 +563,19 @@ func TestDelete1(t *testing.T) {
}
}
func benchmarkDelSeq1e3(b *testing.B) {
func BenchmarkDelSeq1e3(b *testing.B) {
benchmarkDelSeq(b, 1e3)
}
func benchmarkDelSeq1e4(b *testing.B) {
func BenchmarkDelSeq1e4(b *testing.B) {
benchmarkDelSeq(b, 1e4)
}
func benchmarkDelSeq1e5(b *testing.B) {
func BenchmarkDelSeq1e5(b *testing.B) {
benchmarkDelSeq(b, 1e5)
}
func benchmarkDelSeq1e6(b *testing.B) {
func BenchmarkDelSeq1e6(b *testing.B) {
benchmarkDelSeq(b, 1e6)
}
......@@ -624,6 +630,8 @@ func benchmarkDelRnd(b *testing.B, n int) {
for _, v := range a {
r.Delete(v)
}
b.StopTimer()
r.Close()
}
b.StopTimer()
}
......@@ -821,8 +829,11 @@ func benchmarkSeekSeq(b *testing.B, n int) {
debug.FreeOSMemory()
b.StartTimer()
for j := 0; j < n; j++ {
t.Seek(j)
e, _ := t.Seek(j)
e.Close()
}
b.StopTimer()
t.Close()
}
b.StopTimer()
}
......@@ -857,9 +868,12 @@ func benchmarkSeekRnd(b *testing.B, n int) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, v := range a {
r.Seek(v)
e, _ := r.Seek(v)
e.Close()
}
}
b.StopTimer()
r.Close()
}
func BenchmarkNext1e3(b *testing.B) {
......@@ -902,6 +916,8 @@ func benchmarkNext(b *testing.B, n int) {
b.Fatal(m)
}
}
b.StopTimer()
t.Close()
}
func BenchmarkPrev1e3(b *testing.B) {
......
// Copyright 2013 The Go Authors. All rights reserved.
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
......@@ -6,6 +6,8 @@
//
// Changelog
//
// 2014-06-26: Lower GC presure by recycling things.
//
// 2014-04-18: Added new method Put.
//
// Generic types
......@@ -18,7 +20,7 @@
//
// $ make generic
//
// This command will write to stdout a version of the btree.go file where
// This command will write to stdout a version of the bt.go file where
// every key type occurrence is replaced by the word 'key' (written in all
// CAPS) and every value type occurrence is replaced by the word 'value'
// (written in all CAPS). Then you have to replace these tokens with your
......@@ -34,7 +36,7 @@
// $
// $ make generic | sed -e 's/key/int/g' -e 's/value/int/g' > example/int.go
//
// No other changes to int.go are (strictly) necessary, it compiles just fine.
// No other changes to int.go are necessary, it compiles just fine.
//
// Running the benchmarks for 1000 keys on a machine with Intel i5-4670 CPU @
// 3.4GHz, Go release 1.3.
......@@ -57,10 +59,9 @@ package b
import (
"fmt"
"io"
"sync"
)
//TODO check vs orig initialize/finalize
const (
kx = 32 //TODO benchmark tune this number if using custom key/value type(s).
kd = 32 //TODO benchmark tune this number if using custom key/value type(s).
......@@ -76,6 +77,29 @@ func init() {
}
}
var (
btDPool = sync.Pool{New: func() interface{} { return &d{} }}
btEPool = btEpool{sync.Pool{New: func() interface{} { return &Enumerator{} }}}
btTPool = btTpool{sync.Pool{New: func() interface{} { return &Tree{} }}}
btXPool = sync.Pool{New: func() interface{} { return &x{} }}
)
type btTpool struct{ sync.Pool }
func (p *btTpool) get(cmp Cmp) *Tree {
x := p.Get().(*Tree)
x.cmp = cmp
return x
}
type btEpool struct{ sync.Pool }
func (p *btEpool) get(err error, hit bool, i int, k interface{} /*K*/, q *d, t *Tree, ver int64) *Enumerator {
x := p.Get().(*Enumerator)
x.err, x.hit, x.i, x.k, x.q, x.t, x.ver = err, hit, i, k, q, t, ver
return x
}
type (
// Cmp compares a and b. Return value is:
//
......@@ -139,9 +163,11 @@ type (
var ( // R/O zero values
zd d
zde de
ze Enumerator
zk interface{} /*K*/
zt Tree
zx x
zxe xe
zk interface{} /*K*/
)
func clr(q interface{}) {
......@@ -150,16 +176,18 @@ func clr(q interface{}) {
for i := 0; i <= x.c; i++ { // Ch0 Sep0 ... Chn-1 Sepn-1 Chn
clr(x.x[i].ch)
}
*x = zx // GC
*x = zx
btXPool.Put(x)
case *d:
*x = zd // GC
*x = zd
btDPool.Put(x)
}
}
// -------------------------------------------------------------------------- x
func newX(ch0 interface{}) *x {
r := &x{}
r := btXPool.Get().(*x)
r.x[0].ch = ch0
return r
}
......@@ -221,7 +249,7 @@ func (l *d) mvR(r *d, c int) {
// TreeNew returns a newly created, empty Tree. The compare function is used
// for key collation.
func TreeNew(cmp Cmp) *Tree {
return &Tree{cmp: cmp}
return btTPool.get(cmp)
}
// Clear removes all K/V pairs from the tree.
......@@ -235,6 +263,14 @@ func (t *Tree) Clear() {
t.ver++
}
// Close performs Clear and recycles t to a pool for possible later reuse. No
// references to t should exist or such references must not be used afterwards.
func (t *Tree) Close() {
t.Clear()
*t = zt
btTPool.Put(t)
}
func (t *Tree) cat(p *x, q, r *d, pi int) {
t.ver++
q.mvL(r, r.c)
......@@ -243,11 +279,21 @@ func (t *Tree) cat(p *x, q, r *d, pi int) {
} else {
t.last = q
}
q.n = r.n //TODO recycle r
q.n = r.n
*r = zd
btDPool.Put(r)
if p.c > 1 {
p.extract(pi)
p.x[pi].ch = q
} else { //TODO recycle r
} else {
switch x := t.r.(type) {
case *x:
*x = zx
btXPool.Put(x)
case *d:
*x = zd
btDPool.Put(x)
}
t.r = q
}
}
......@@ -257,7 +303,9 @@ func (t *Tree) catX(p, q, r *x, pi int) {
q.x[q.c].k = p.x[pi].k
copy(q.x[q.c+1:], r.x[:r.c])
q.c += r.c + 1
q.x[q.c].ch = r.x[r.c].ch //TODO recycle r
q.x[q.c].ch = r.x[r.c].ch
*r = zx
btXPool.Put(r)
if p.c > 1 {
p.c--
pc := p.c
......@@ -271,7 +319,15 @@ func (t *Tree) catX(p, q, r *x, pi int) {
return
}
t.r = q //TODO recycle r
switch x := t.r.(type) {
case *x:
*x = zx
btXPool.Put(x)
case *d:
*x = zd
btDPool.Put(x)
}
t.r = q
}
// Delete removes the k's KV pair, if it exists, in which case Delete returns
......@@ -291,7 +347,7 @@ func (t *Tree) Delete(k interface{} /*K*/) (ok bool) {
switch x := q.(type) {
case *x:
if x.c < kx && q != t.r {
t.underflowX(p, &x, pi, &i)
x, i = t.underflowX(p, x, pi, i)
}
pi = i + 1
p = x
......@@ -316,7 +372,7 @@ func (t *Tree) Delete(k interface{} /*K*/) (ok bool) {
switch x := q.(type) {
case *x:
if x.c < kx && q != t.r {
t.underflowX(p, &x, pi, &i)
x, i = t.underflowX(p, x, pi, i)
}
pi = i
p = x
......@@ -473,7 +529,7 @@ func (t *Tree) overflow(p *x, q *d, pi, i int, k interface{} /*K*/, v interface{
func (t *Tree) Seek(k interface{} /*K*/) (e *Enumerator, ok bool) {
q := t.r
if q == nil {
e = &Enumerator{nil, false, 0, k, nil, t, t.ver}
e = btEPool.get(nil, false, 0, k, nil, t, t.ver)
return
}
......@@ -485,16 +541,15 @@ func (t *Tree) Seek(k interface{} /*K*/) (e *Enumerator, ok bool) {
q = x.x[i+1].ch
continue
case *d:
e = &Enumerator{nil, ok, i, k, x, t, t.ver}
return
return btEPool.get(nil, ok, i, k, x, t, t.ver), true
}
}
switch x := q.(type) {
case *x:
q = x.x[i].ch
case *d:
e = &Enumerator{nil, ok, i, k, x, t, t.ver}
return
return btEPool.get(nil, ok, i, k, x, t, t.ver), false
}
}
}
......@@ -507,7 +562,7 @@ func (t *Tree) SeekFirst() (e *Enumerator, err error) {
return nil, io.EOF
}
return &Enumerator{nil, true, 0, q.d[0].k, q, t, t.ver}, nil
return btEPool.get(nil, true, 0, q.d[0].k, q, t, t.ver), nil
}
// SeekLast returns an enumerator positioned on the last KV pair in the tree,
......@@ -518,7 +573,7 @@ func (t *Tree) SeekLast() (e *Enumerator, err error) {
return nil, io.EOF
}
return &Enumerator{nil, true, q.c - 1, q.d[q.c-1].k, q, t, t.ver}, nil
return btEPool.get(nil, true, q.c-1, q.d[q.c-1].k, q, t, t.ver), nil
}
// Set sets the value associated with k.
......@@ -531,7 +586,7 @@ func (t *Tree) Set(k interface{} /*K*/, v interface{} /*V*/) {
var p *x
q := t.r
if q == nil {
z := t.insert(&d{}, 0, k, v)
z := t.insert(btDPool.Get().(*d), 0, k, v)
t.r, t.first, t.last = z, z, z
return
}
......@@ -542,7 +597,7 @@ func (t *Tree) Set(k interface{} /*K*/, v interface{} /*V*/) {
switch x := q.(type) {
case *x:
if x.c > 2*kx {
t.splitX(p, &x, pi, &i)
x, i = t.splitX(p, x, pi, i)
}
pi = i + 1
p = x
......@@ -557,7 +612,7 @@ func (t *Tree) Set(k interface{} /*K*/, v interface{} /*V*/) {
switch x := q.(type) {
case *x:
if x.c > 2*kx {
t.splitX(p, &x, pi, &i)
x, i = t.splitX(p, x, pi, i)
}
pi = i
p = x
......@@ -598,7 +653,7 @@ func (t *Tree) Put(k interface{} /*K*/, upd func(oldV interface{} /*V*/, exists
switch x := q.(type) {
case *x:
if x.c > 2*kx {
t.splitX(p, &x, pi, &i)
x, i = t.splitX(p, x, pi, i)
}
pi = i + 1
p = x
......@@ -619,7 +674,7 @@ func (t *Tree) Put(k interface{} /*K*/, upd func(oldV interface{} /*V*/, exists
switch x := q.(type) {
case *x:
if x.c > 2*kx {
t.splitX(p, &x, pi, &i)
x, i = t.splitX(p, x, pi, i)
}
pi = i
p = x
......@@ -647,14 +702,14 @@ func (t *Tree) Put(k interface{} /*K*/, upd func(oldV interface{} /*V*/, exists
return
}
z := t.insert(&d{}, 0, k, newV)
z := t.insert(btDPool.Get().(*d), 0, k, newV)
t.r, t.first, t.last = z, z, z
return
}
func (t *Tree) split(p *x, q *d, pi, i int, k interface{} /*K*/, v interface{} /*V*/) {
t.ver++
r := &d{}
r := btDPool.Get().(*d)
if q.n != nil {
r.n = q.n
r.n.p = r
......@@ -687,10 +742,9 @@ func (t *Tree) split(p *x, q *d, pi, i int, k interface{} /*K*/, v interface{} /
t.insert(q, i, k, v)
}
func (t *Tree) splitX(p *x, pp **x, pi int, i *int) {
func (t *Tree) splitX(p *x, q *x, pi int, i int) (*x, int) {
t.ver++
q := *pp
r := &x{}
r := btXPool.Get().(*x)
copy(r.x[:], q.x[kx+1:])
q.c = kx
r.c = kx
......@@ -703,10 +757,11 @@ func (t *Tree) splitX(p *x, pp **x, pi int, i *int) {
for i := range q.x[kx+1:] {
q.x[kx+i+1] = zxe
}
if *i > kx {
*pp = r
*i -= kx + 1
if i > kx {
q = r
i -= kx + 1
}
return q, i
}
func (t *Tree) underflow(p *x, q *d, pi int) {
......@@ -727,10 +782,9 @@ func (t *Tree) underflow(p *x, q *d, pi int) {
}
}
func (t *Tree) underflowX(p *x, pp **x, pi int, i *int) {
func (t *Tree) underflowX(p *x, q *x, pi int, i int) (*x, int) {
t.ver++
var l, r *x
q := *pp
if pi >= 0 {
if pi > 0 {
......@@ -747,10 +801,10 @@ func (t *Tree) underflowX(p *x, pp **x, pi int, i *int) {
q.x[0].ch = l.x[l.c].ch
q.x[0].k = p.x[pi-1].k
q.c++
*i++
i++
l.c--
p.x[pi-1].k = l.x[l.c].k
return
return q, i
}
if r != nil && r.c > kx {
......@@ -764,21 +818,29 @@ func (t *Tree) underflowX(p *x, pp **x, pi int, i *int) {
r.x[rc].ch = r.x[rc+1].ch
r.x[rc].k = zk
r.x[rc+1].ch = nil
return
return q, i
}
if l != nil {
*i += l.c + 1
i += l.c + 1
t.catX(p, l, q, pi-1)
*pp = l
return
q = l
return q, i
}
t.catX(p, q, r, pi)
return q, i
}
// ----------------------------------------------------------------- Enumerator
// Close recycles e to a pool for possible later reuse. No references to e
// should exist or such references must not be used afterwards.
func (e *Enumerator) Close() {
*e = ze
btEPool.Put(e)
}
// Next returns the currently enumerated item, if it exists and moves to the
// next item in the key collation order. If there is no item to return, err ==
// io.EOF is returned.
......@@ -796,6 +858,7 @@ func (e *Enumerator) Next() (k interface{} /*K*/, v interface{} /*V*/, err error
}
*e = *f
f.Close()
}
if e.q == nil {
e.err, err = io.EOF, io.EOF
......@@ -849,6 +912,7 @@ func (e *Enumerator) Prev() (k interface{} /*K*/, v interface{} /*V*/, err error
}
*e = *f
f.Close()
}
if e.q == nil {
e.err, err = io.EOF, io.EOF
......
.PHONY: all todo clean cover
.PHONY: all todo clean cover mem
testbin=b.test
all: editor
go build
......@@ -10,6 +12,11 @@ editor:
go test -i
go test
mem:
go test -c
./$(testbin) -test.bench . -test.memprofile mem.out -test.memprofilerate 1
go tool pprof --lines --web --alloc_space $(testbin) mem.out
todo:
@grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* *.go || true
@grep -n TODO *.go || true
......
// Copyright 2013 The Go Authors. All rights reserved.
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
......@@ -131,10 +131,6 @@ func TestSetGet1(t *testing.T) {
}
}
func BenchmarkSetSeq1e2(b *testing.B) {
benchmarkSetSeq(b, 1e2)
}
func BenchmarkSetSeq1e3(b *testing.B) {
benchmarkSetSeq(b, 1e3)
}
......@@ -161,14 +157,12 @@ func benchmarkSetSeq(b *testing.B, n int) {
for j := 0; j < n; j++ {
r.Set(j, j)
}
b.StopTimer()
r.Close()
}
b.StopTimer()
}
func BenchmarkGetSeq1e2(b *testing.B) {
benchmarkGetSeq(b, 1e2)
}
func BenchmarkGetSeq1e3(b *testing.B) {
benchmarkGetSeq(b, 1e3)
}
......@@ -198,10 +192,7 @@ func benchmarkGetSeq(b *testing.B, n int) {
}
}
b.StopTimer()
}
func BenchmarkSetRnd1e2(b *testing.B) {
benchmarkSetRnd(b, 1e2)
r.Close()
}
func BenchmarkSetRnd1e3(b *testing.B) {
......@@ -235,14 +226,12 @@ func benchmarkSetRnd(b *testing.B, n int) {
for _, v := range a {
r.Set(v, 0)
}
b.StopTimer()
r.Close()
}
b.StopTimer()
}
func BenchmarkGetRnd1e2(b *testing.B) {
benchmarkGetRnd(b, 1e2)
}
func BenchmarkGetRnd1e3(b *testing.B) {
benchmarkGetRnd(b, 1e3)
}
......@@ -277,6 +266,7 @@ func benchmarkGetRnd(b *testing.B, n int) {
}
}
b.StopTimer()
r.Close()
}
func TestSetGet2(t *testing.T) {
......@@ -451,23 +441,19 @@ func TestDelete1(t *testing.T) {
}
}
func benchmarkDelSeq1e2(b *testing.B) {
benchmarkDelSeq(b, 1e2)
}
func benchmarkDelSeq1e3(b *testing.B) {
func BenchmarkDelSeq1e3(b *testing.B) {
benchmarkDelSeq(b, 1e3)
}
func benchmarkDelSeq1e4(b *testing.B) {
func BenchmarkDelSeq1e4(b *testing.B) {
benchmarkDelSeq(b, 1e4)
}
func benchmarkDelSeq1e5(b *testing.B) {
func BenchmarkDelSeq1e5(b *testing.B) {
benchmarkDelSeq(b, 1e5)
}
func benchmarkDelSeq1e6(b *testing.B) {
func BenchmarkDelSeq1e6(b *testing.B) {
benchmarkDelSeq(b, 1e6)
}
......@@ -484,14 +470,12 @@ func benchmarkDelSeq(b *testing.B, n int) {
for j := 0; j < n; j++ {
r.Delete(j)
}
b.StopTimer()
r.Close()
}
b.StopTimer()
}
func BenchmarkDelRnd1e2(b *testing.B) {
benchmarkDelRnd(b, 1e2)
}
func BenchmarkDelRnd1e3(b *testing.B) {
benchmarkDelRnd(b, 1e3)
}
......@@ -526,6 +510,8 @@ func benchmarkDelRnd(b *testing.B, n int) {
for _, v := range a {
r.Delete(v)
}
b.StopTimer()
r.Close()
}
b.StopTimer()
}
......@@ -697,10 +683,6 @@ func TestEnumeratorPrev(t *testing.T) {
}
}
func BenchmarkSeekSeq1e2(b *testing.B) {
benchmarkSeekSeq(b, 1e2)
}
func BenchmarkSeekSeq1e3(b *testing.B) {
benchmarkSeekSeq(b, 1e3)
}
......@@ -727,16 +709,15 @@ func benchmarkSeekSeq(b *testing.B, n int) {
debug.FreeOSMemory()
b.StartTimer()
for j := 0; j < n; j++ {
t.Seek(j)
e, _ := t.Seek(j)
e.Close()
}
b.StopTimer()
t.Close()
}
b.StopTimer()
}
func BenchmarkSeekRnd1e2(b *testing.B) {
benchmarkSeekRnd(b, 1e2)
}
func BenchmarkSeekRnd1e3(b *testing.B) {
benchmarkSeekRnd(b, 1e3)
}
......@@ -767,13 +748,12 @@ func benchmarkSeekRnd(b *testing.B, n int) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, v := range a {
r.Seek(v)
e, _ := r.Seek(v)
e.Close()
}
}
}
func BenchmarkNext1e2(b *testing.B) {
benchmarkNext(b, 1e2)
b.StopTimer()
r.Close()
}
func BenchmarkNext1e3(b *testing.B) {
......@@ -816,10 +796,8 @@ func benchmarkNext(b *testing.B, n int) {
b.Fatal(m)
}
}
}
func BenchmarkPrev1e2(b *testing.B) {
benchmarkPrev(b, 1e2)
b.StopTimer()
t.Close()
}
func BenchmarkPrev1e3(b *testing.B) {
......@@ -862,6 +840,8 @@ func benchmarkPrev(b *testing.B, n int) {
b.Fatal(m)
}
}
b.StopTimer()
t.Close()
}
func TestSeekFirst0(t *testing.T) {
......
// Copyright 2013 The Go Authors. All rights reserved.
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
......@@ -8,10 +8,9 @@ package b
import (
"fmt"
"io"
"sync"
)
//TODO check vs orig initialize/finalize
const (
kx = 32 //TODO benchmark tune this number if using custom key/value type(s).
kd = 32 //TODO benchmark tune this number if using custom key/value type(s).
......@@ -27,6 +26,29 @@ func init() {
}
}
var (
btDPool = sync.Pool{New: func() interface{} { return &d{} }}
btEPool = btEpool{sync.Pool{New: func() interface{} { return &Enumerator{} }}}
btTPool = btTpool{sync.Pool{New: func() interface{} { return &Tree{} }}}
btXPool = sync.Pool{New: func() interface{} { return &x{} }}
)
type btTpool struct{ sync.Pool }
func (p *btTpool) get(cmp Cmp) *Tree {
x := p.Get().(*Tree)
x.cmp = cmp
return x
}
type btEpool struct{ sync.Pool }
func (p *btEpool) get(err error, hit bool, i int, k int, q *d, t *Tree, ver int64) *Enumerator {
x := p.Get().(*Enumerator)
x.err, x.hit, x.i, x.k, x.q, x.t, x.ver = err, hit, i, k, q, t, ver
return x
}
type (
// Cmp compares a and b. Return value is:
//
......@@ -90,9 +112,11 @@ type (
var ( // R/O zero values
zd d
zde de
ze Enumerator
zk int
zt Tree
zx x
zxe xe
zk int
)
func clr(q interface{}) {
......@@ -101,16 +125,18 @@ func clr(q interface{}) {
for i := 0; i <= x.c; i++ { // Ch0 Sep0 ... Chn-1 Sepn-1 Chn
clr(x.x[i].ch)
}
*x = zx // GC
*x = zx
btXPool.Put(x)
case *d:
*x = zd // GC
*x = zd
btDPool.Put(x)
}
}
// -------------------------------------------------------------------------- x
func newX(ch0 interface{}) *x {
r := &x{}
r := btXPool.Get().(*x)
r.x[0].ch = ch0
return r
}
......@@ -172,7 +198,7 @@ func (l *d) mvR(r *d, c int) {
// TreeNew returns a newly created, empty Tree. The compare function is used
// for key collation.
func TreeNew(cmp Cmp) *Tree {
return &Tree{cmp: cmp}
return btTPool.get(cmp)
}
// Clear removes all K/V pairs from the tree.
......@@ -186,6 +212,14 @@ func (t *Tree) Clear() {
t.ver++
}
// Close performs Clear and recycles t to a pool for possible later reuse. No
// references to t should exist or such references must not be used afterwards.
func (t *Tree) Close() {
t.Clear()
*t = zt
btTPool.Put(t)
}
func (t *Tree) cat(p *x, q, r *d, pi int) {
t.ver++
q.mvL(r, r.c)
......@@ -194,11 +228,21 @@ func (t *Tree) cat(p *x, q, r *d, pi int) {
} else {
t.last = q
}
q.n = r.n //TODO recycle r
q.n = r.n
*r = zd
btDPool.Put(r)
if p.c > 1 {
p.extract(pi)
p.x[pi].ch = q
} else { //TODO recycle r
} else {
switch x := t.r.(type) {
case *x:
*x = zx
btXPool.Put(x)
case *d:
*x = zd
btDPool.Put(x)
}
t.r = q
}
}
......@@ -208,7 +252,9 @@ func (t *Tree) catX(p, q, r *x, pi int) {
q.x[q.c].k = p.x[pi].k
copy(q.x[q.c+1:], r.x[:r.c])
q.c += r.c + 1
q.x[q.c].ch = r.x[r.c].ch //TODO recycle r
q.x[q.c].ch = r.x[r.c].ch
*r = zx
btXPool.Put(r)
if p.c > 1 {
p.c--
pc := p.c
......@@ -222,7 +268,15 @@ func (t *Tree) catX(p, q, r *x, pi int) {
return
}
t.r = q //TODO recycle r
switch x := t.r.(type) {
case *x:
*x = zx
btXPool.Put(x)
case *d:
*x = zd
btDPool.Put(x)
}
t.r = q
}
// Delete removes the k's KV pair, if it exists, in which case Delete returns
......@@ -242,7 +296,7 @@ func (t *Tree) Delete(k int) (ok bool) {
switch x := q.(type) {
case *x:
if x.c < kx && q != t.r {
t.underflowX(p, &x, pi, &i)
x, i = t.underflowX(p, x, pi, i)
}
pi = i + 1
p = x
......@@ -267,7 +321,7 @@ func (t *Tree) Delete(k int) (ok bool) {
switch x := q.(type) {
case *x:
if x.c < kx && q != t.r {
t.underflowX(p, &x, pi, &i)
x, i = t.underflowX(p, x, pi, i)
}
pi = i
p = x
......@@ -424,7 +478,7 @@ func (t *Tree) overflow(p *x, q *d, pi, i int, k int, v int) {
func (t *Tree) Seek(k int) (e *Enumerator, ok bool) {
q := t.r
if q == nil {
e = &Enumerator{nil, false, 0, k, nil, t, t.ver}
e = btEPool.get(nil, false, 0, k, nil, t, t.ver)
return
}
......@@ -436,16 +490,15 @@ func (t *Tree) Seek(k int) (e *Enumerator, ok bool) {
q = x.x[i+1].ch
continue
case *d:
e = &Enumerator{nil, ok, i, k, x, t, t.ver}
return
return btEPool.get(nil, ok, i, k, x, t, t.ver), true
}
}
switch x := q.(type) {
case *x:
q = x.x[i].ch
case *d:
e = &Enumerator{nil, ok, i, k, x, t, t.ver}
return
return btEPool.get(nil, ok, i, k, x, t, t.ver), false
}
}
}
......@@ -458,7 +511,7 @@ func (t *Tree) SeekFirst() (e *Enumerator, err error) {
return nil, io.EOF
}
return &Enumerator{nil, true, 0, q.d[0].k, q, t, t.ver}, nil
return btEPool.get(nil, true, 0, q.d[0].k, q, t, t.ver), nil
}
// SeekLast returns an enumerator positioned on the last KV pair in the tree,
......@@ -469,7 +522,7 @@ func (t *Tree) SeekLast() (e *Enumerator, err error) {
return nil, io.EOF
}
return &Enumerator{nil, true, q.c - 1, q.d[q.c-1].k, q, t, t.ver}, nil
return btEPool.get(nil, true, q.c-1, q.d[q.c-1].k, q, t, t.ver), nil
}
// Set sets the value associated with k.
......@@ -482,7 +535,7 @@ func (t *Tree) Set(k int, v int) {
var p *x
q := t.r
if q == nil {
z := t.insert(&d{}, 0, k, v)
z := t.insert(btDPool.Get().(*d), 0, k, v)
t.r, t.first, t.last = z, z, z
return
}
......@@ -493,7 +546,7 @@ func (t *Tree) Set(k int, v int) {
switch x := q.(type) {
case *x:
if x.c > 2*kx {
t.splitX(p, &x, pi, &i)
x, i = t.splitX(p, x, pi, i)
}
pi = i + 1
p = x
......@@ -508,7 +561,7 @@ func (t *Tree) Set(k int, v int) {
switch x := q.(type) {
case *x:
if x.c > 2*kx {
t.splitX(p, &x, pi, &i)
x, i = t.splitX(p, x, pi, i)
}
pi = i
p = x
......@@ -549,7 +602,7 @@ func (t *Tree) Put(k int, upd func(oldV int, exists bool) (newV int, write bool)
switch x := q.(type) {
case *x:
if x.c > 2*kx {
t.splitX(p, &x, pi, &i)
x, i = t.splitX(p, x, pi, i)
}
pi = i + 1
p = x
......@@ -570,7 +623,7 @@ func (t *Tree) Put(k int, upd func(oldV int, exists bool) (newV int, write bool)
switch x := q.(type) {
case *x:
if x.c > 2*kx {
t.splitX(p, &x, pi, &i)
x, i = t.splitX(p, x, pi, i)
}
pi = i
p = x
......@@ -598,14 +651,14 @@ func (t *Tree) Put(k int, upd func(oldV int, exists bool) (newV int, write bool)
return
}
z := t.insert(&d{}, 0, k, newV)
z := t.insert(btDPool.Get().(*d), 0, k, newV)
t.r, t.first, t.last = z, z, z
return
}
func (t *Tree) split(p *x, q *d, pi, i int, k int, v int) {
t.ver++
r := &d{}
r := btDPool.Get().(*d)
if q.n != nil {
r.n = q.n
r.n.p = r
......@@ -638,10 +691,9 @@ func (t *Tree) split(p *x, q *d, pi, i int, k int, v int) {
t.insert(q, i, k, v)
}
func (t *Tree) splitX(p *x, pp **x, pi int, i *int) {
func (t *Tree) splitX(p *x, q *x, pi int, i int) (*x, int) {
t.ver++
q := *pp
r := &x{}
r := btXPool.Get().(*x)
copy(r.x[:], q.x[kx+1:])
q.c = kx
r.c = kx
......@@ -654,10 +706,11 @@ func (t *Tree) splitX(p *x, pp **x, pi int, i *int) {
for i := range q.x[kx+1:] {
q.x[kx+i+1] = zxe
}
if *i > kx {
*pp = r
*i -= kx + 1
if i > kx {
q = r
i -= kx + 1
}
return q, i
}
func (t *Tree) underflow(p *x, q *d, pi int) {
......@@ -678,10 +731,9 @@ func (t *Tree) underflow(p *x, q *d, pi int) {
}
}
func (t *Tree) underflowX(p *x, pp **x, pi int, i *int) {
func (t *Tree) underflowX(p *x, q *x, pi int, i int) (*x, int) {
t.ver++
var l, r *x
q := *pp
if pi >= 0 {
if pi > 0 {
......@@ -698,10 +750,10 @@ func (t *Tree) underflowX(p *x, pp **x, pi int, i *int) {
q.x[0].ch = l.x[l.c].ch
q.x[0].k = p.x[pi-1].k
q.c++
*i++
i++
l.c--
p.x[pi-1].k = l.x[l.c].k
return
return q, i
}
if r != nil && r.c > kx {
......@@ -715,21 +767,29 @@ func (t *Tree) underflowX(p *x, pp **x, pi int, i *int) {
r.x[rc].ch = r.x[rc+1].ch
r.x[rc].k = zk
r.x[rc+1].ch = nil
return
return q, i
}
if l != nil {
*i += l.c + 1
i += l.c + 1
t.catX(p, l, q, pi-1)
*pp = l
return
q = l
return q, i
}
t.catX(p, q, r, pi)
return q, i
}
// ----------------------------------------------------------------- Enumerator
// Close recycles e to a pool for possible later reuse. No references to e
// should exist or such references must not be used afterwards.
func (e *Enumerator) Close() {
*e = ze
btEPool.Put(e)
}
// Next returns the currently enumerated item, if it exists and moves to the
// next item in the key collation order. If there is no item to return, err ==
// io.EOF is returned.
......@@ -747,6 +807,7 @@ func (e *Enumerator) Next() (k int, v int, err error) {
}
*e = *f
f.Close()
}
if e.q == nil {
e.err, err = io.EOF, io.EOF
......@@ -800,6 +861,7 @@ func (e *Enumerator) Prev() (k int, v int, err error) {
}
*e = *f
f.Close()
}
if e.q == nil {
e.err, err = io.EOF, io.EOF
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment