aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Johnson <benbjohnson@yahoo.com>2014-05-23 10:36:23 -0600
committerBen Johnson <benbjohnson@yahoo.com>2014-05-23 10:40:09 -0600
commit06b01dbb67dde581977db43d0b36c19251f83af4 (patch)
treec2f5272525bb44a96097f5b0c7ea398856ca8f40
parentFix freelist allocate(). (diff)
downloaddedo-06b01dbb67dde581977db43d0b36c19251f83af4.tar.gz
dedo-06b01dbb67dde581977db43d0b36c19251f83af4.tar.xz
Remove allocations from read-only buckets.
-rw-r--r--Makefile2
-rw-r--r--bucket.go23
-rw-r--r--cmd/bolt/bench.go83
3 files changed, 101 insertions, 7 deletions
diff --git a/Makefile b/Makefile
index 67635ae..3fec2c7 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,8 @@ BRANCH=`git rev-parse --abbrev-ref HEAD`
COMMIT=`git rev-parse --short HEAD`
GOLDFLAGS="-X main.branch $(BRANCH) -X main.commit $(COMMIT)"
+default: build
+
bench:
go test -v -test.run=NOTHINCONTAINSTHIS -test.bench=$(BENCH)
diff --git a/bucket.go b/bucket.go
index 877c310..2338a8b 100644
--- a/bucket.go
+++ b/bucket.go
@@ -76,8 +76,8 @@ type bucket struct {
// newBucket returns a new bucket associated with a transaction.
func newBucket(tx *Tx) Bucket {
var b = Bucket{tx: tx}
- b.buckets = make(map[string]*Bucket)
if tx.writable {
+ b.buckets = make(map[string]*Bucket)
b.nodes = make(map[pgid]*node)
}
return b
@@ -115,8 +115,10 @@ func (b *Bucket) Cursor() *Cursor {
// Bucket retrieves a nested bucket by name.
// Returns nil if the bucket does not exist.
func (b *Bucket) Bucket(name []byte) *Bucket {
- if child := b.buckets[string(name)]; child != nil {
- return child
+ if b.buckets != nil {
+ if child := b.buckets[string(name)]; child != nil {
+ return child
+ }
}
// Move cursor to key.
@@ -130,7 +132,9 @@ func (b *Bucket) Bucket(name []byte) *Bucket {
// Otherwise create a bucket and cache it.
var child = b.openBucket(v)
- b.buckets[string(name)] = child
+ if b.buckets != nil {
+ b.buckets[string(name)] = child
+ }
return child
}
@@ -139,8 +143,15 @@ func (b *Bucket) Bucket(name []byte) *Bucket {
// from a parent into a Bucket
func (b *Bucket) openBucket(value []byte) *Bucket {
var child = newBucket(b.tx)
- child.bucket = &bucket{}
- *child.bucket = *(*bucket)(unsafe.Pointer(&value[0]))
+
+ // If this is a writable transaction then we need to copy the bucket entry.
+ // Read-only transactions can point directly at the mmap entry.
+ if b.tx.writable {
+ child.bucket = &bucket{}
+ *child.bucket = *(*bucket)(unsafe.Pointer(&value[0]))
+ } else {
+ child.bucket = (*bucket)(unsafe.Pointer(&value[0]))
+ }
// Save a reference to the inline page if the bucket is inline.
if child.root == 0 {
diff --git a/cmd/bolt/bench.go b/cmd/bolt/bench.go
index 6379144..5d0dbec 100644
--- a/cmd/bolt/bench.go
+++ b/cmd/bolt/bench.go
@@ -100,6 +100,10 @@ func benchWrite(db *bolt.DB, options *BenchOptions, results *BenchResults) error
err = benchWriteSequential(db, options, results)
case "rnd":
err = benchWriteRandom(db, options, results)
+ case "seq-nest":
+ err = benchWriteSequentialNested(db, options, results)
+ case "rnd-nest":
+ err = benchWriteRandomNested(db, options, results)
default:
return fmt.Errorf("invalid write mode: %s", options.WriteMode)
}
@@ -119,6 +123,16 @@ func benchWriteRandom(db *bolt.DB, options *BenchOptions, results *BenchResults)
return benchWriteWithSource(db, options, results, func() uint32 { return r.Uint32() })
}
+func benchWriteSequentialNested(db *bolt.DB, options *BenchOptions, results *BenchResults) error {
+ var i = uint32(0)
+ return benchWriteNestedWithSource(db, options, results, func() uint32 { i++; return i })
+}
+
+func benchWriteRandomNested(db *bolt.DB, options *BenchOptions, results *BenchResults) error {
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
+ return benchWriteNestedWithSource(db, options, results, func() uint32 { return r.Uint32() })
+}
+
func benchWriteWithSource(db *bolt.DB, options *BenchOptions, results *BenchResults, keySource func() uint32) error {
results.WriteOps = options.Iterations
@@ -144,6 +158,35 @@ func benchWriteWithSource(db *bolt.DB, options *BenchOptions, results *BenchResu
return nil
}
+func benchWriteNestedWithSource(db *bolt.DB, options *BenchOptions, results *BenchResults, keySource func() uint32) error {
+ results.WriteOps = options.Iterations
+
+ for i := 0; i < options.Iterations; i += options.BatchSize {
+ err := db.Update(func(tx *bolt.Tx) error {
+ top, _ := tx.CreateBucketIfNotExists(benchBucketName)
+
+ var name = make([]byte, options.KeySize)
+ binary.BigEndian.PutUint32(name, keySource())
+ b, _ := top.CreateBucketIfNotExists(name)
+
+ for j := 0; j < options.BatchSize; j++ {
+ var key = make([]byte, options.KeySize)
+ var value = make([]byte, options.ValueSize)
+ binary.BigEndian.PutUint32(key, keySource())
+ if err := b.Put(key, value); err != nil {
+ return err
+ }
+ }
+
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
// Reads from the database.
func benchRead(db *bolt.DB, options *BenchOptions, results *BenchResults) error {
var err error
@@ -151,7 +194,11 @@ func benchRead(db *bolt.DB, options *BenchOptions, results *BenchResults) error
switch options.ReadMode {
case "seq":
- err = benchReadSequential(db, options, results)
+ if options.WriteMode == "seq-nest" || options.WriteMode == "rnd-nest" {
+ err = benchReadSequentialNested(db, options, results)
+ } else {
+ err = benchReadSequential(db, options, results)
+ }
default:
return fmt.Errorf("invalid read mode: %s", options.ReadMode)
}
@@ -191,6 +238,40 @@ func benchReadSequential(db *bolt.DB, options *BenchOptions, results *BenchResul
})
}
+func benchReadSequentialNested(db *bolt.DB, options *BenchOptions, results *BenchResults) error {
+ return db.View(func(tx *bolt.Tx) error {
+ var t = time.Now()
+
+ for {
+ var count int
+ var top = tx.Bucket(benchBucketName)
+ top.ForEach(func(name, _ []byte) error {
+ c := top.Bucket(name).Cursor()
+ for k, v := c.First(); k != nil; k, v = c.Next() {
+ if v == nil {
+ return errors.New("invalid value")
+ }
+ count++
+ }
+ return nil
+ })
+
+ if options.WriteMode == "seq-nest" && count != options.Iterations {
+ return fmt.Errorf("read seq-nest: iter mismatch: expected %d, got %d", options.Iterations, count)
+ }
+
+ results.ReadOps += count
+
+ // Make sure we do this for at least a second.
+ if time.Since(t) >= time.Second {
+ break
+ }
+ }
+
+ return nil
+ })
+}
+
// Starts all profiles set on the options.
func benchStartProfiling(options *BenchOptions) {
var err error