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) {
......
This diff is collapsed.
.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