diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..215e0cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.prof +*.test diff --git a/Makefile b/Makefile index 29ea2f5..4cc71b6 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,10 @@ cover: fmt go tool cover -html=$(COVERPROFILE) rm $(COVERPROFILE) +cpuprofile: fmt + @go test -c + @./bolt.test -test.v -test.run="^X" -test.bench=$(BENCH) -test.cpuprofile cpu.prof + fmt: @go fmt ./... @@ -25,4 +29,4 @@ test: fmt @echo "=== RACE DETECTOR ===" @go test -v -race -test.run=Parallel -.PHONY: bench cloc cover fmt test +.PHONY: bench cloc cover cpuprofile fmt memprofile test diff --git a/db_test.go b/db_test.go index 2b7975c..c8a8267 100644 --- a/db_test.go +++ b/db_test.go @@ -3,8 +3,10 @@ package bolt import ( "io" "io/ioutil" + "math/rand" "os" "strconv" + "strings" "syscall" "testing" "time" @@ -341,6 +343,29 @@ func TestDBString(t *testing.T) { assert.Equal(t, db.GoString(), `bolt.DB{path:"/tmp/foo"}`) } +// Benchmark the performance of single put transactions in random order. +func BenchmarkDBPutSequential(b *testing.B) { + value := []byte(strings.Repeat("0", 64)) + withOpenDB(func(db *DB, path string) { + db.CreateBucket("widgets") + for i := 0; i < b.N; i++ { + db.Put("widgets", []byte(strconv.Itoa(i)), value) + } + }) +} + +// Benchmark the performance of single put transactions in random order. +func BenchmarkDBPutRandom(b *testing.B) { + indexes := rand.Perm(b.N) + value := []byte(strings.Repeat("0", 64)) + withOpenDB(func(db *DB, path string) { + db.CreateBucket("widgets") + for i := 0; i < b.N; i++ { + db.Put("widgets", []byte(strconv.Itoa(indexes[i])), value) + } + }) +} + // withDB executes a function with a database reference. func withDB(fn func(*DB, string)) { f, _ := ioutil.TempFile("", "bolt-") diff --git a/rwtransaction_test.go b/rwtransaction_test.go index 1635b45..e45ec10 100644 --- a/rwtransaction_test.go +++ b/rwtransaction_test.go @@ -1,6 +1,8 @@ package bolt import ( + "math/rand" + "strconv" "strings" "testing" @@ -140,3 +142,40 @@ func TestRWTransactionDeleteBucketNotFound(t *testing.T) { assert.Equal(t, err, ErrBucketNotFound) }) } + +// Benchmark the performance of bulk put transactions in random order. +func BenchmarkRWTransactionPutRandom(b *testing.B) { + indexes := rand.Perm(b.N) + value := []byte(strings.Repeat("0", 64)) + withOpenDB(func(db *DB, path string) { + db.CreateBucket("widgets") + var txn *RWTransaction + var bucket *Bucket + for i := 0; i < b.N; i++ { + if i%1000 == 0 { + if txn != nil { + txn.Commit() + } + txn, _ = db.RWTransaction() + bucket = txn.Bucket("widgets") + } + bucket.Put([]byte(strconv.Itoa(indexes[i])), value) + } + txn.Commit() + }) +} + +// Benchmark the performance of bulk put transactions in sequential order. +func BenchmarkRWTransactionPutSequential(b *testing.B) { + value := []byte(strings.Repeat("0", 64)) + withOpenDB(func(db *DB, path string) { + db.CreateBucket("widgets") + db.Do(func(txn *RWTransaction) error { + bucket := txn.Bucket("widgets") + for i := 0; i < b.N; i++ { + bucket.Put([]byte(strconv.Itoa(i)), value) + } + return nil + }) + }) +} diff --git a/transaction_test.go b/transaction_test.go index 8cf9dfa..04cb44b 100644 --- a/transaction_test.go +++ b/transaction_test.go @@ -2,8 +2,11 @@ package bolt import ( "fmt" + "math/rand" "os" "sort" + "strconv" + "strings" "testing" "testing/quick" @@ -228,3 +231,35 @@ func TestTransactionCursorIterateReverse(t *testing.T) { } fmt.Fprint(os.Stderr, "\n") } + +// Benchmark the performance iterating over a cursor. +func BenchmarkTransactionCursor(b *testing.B) { + indexes := rand.Perm(b.N) + value := []byte(strings.Repeat("0", 64)) + + withOpenDB(func(db *DB, path string) { + // Write data to bucket. + db.CreateBucket("widgets") + db.Do(func(txn *RWTransaction) error { + bucket := txn.Bucket("widgets") + for i := 0; i < b.N; i++ { + bucket.Put([]byte(strconv.Itoa(indexes[i])), value) + } + return nil + }) + b.ResetTimer() + + // Iterate over bucket using cursor. + db.With(func(txn *Transaction) error { + count := 0 + c := txn.Bucket("widgets").Cursor() + for k, _ := c.First(); k != nil; k, _ = c.Next() { + count++ + } + if count != b.N { + b.Fatalf("wrong count: %d; expected: %d", count, b.N) + } + return nil + }) + }) +}