Skip to content

Commit 0ea65d4

Browse files
authored
ethdb: add benchmark test suite (#26659)
1 parent b0cd8c4 commit 0ea65d4

File tree

4 files changed

+147
-1
lines changed

4 files changed

+147
-1
lines changed

ethdb/dbtest/testsuite.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package dbtest
1818

1919
import (
2020
"bytes"
21+
"math/rand"
2122
"reflect"
2223
"sort"
2324
"testing"
@@ -377,6 +378,101 @@ func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) {
377378
})
378379
}
379380

381+
// BenchDatabaseSuite runs a suite of benchmarks against a KeyValueStore database
382+
// implementation.
383+
func BenchDatabaseSuite(b *testing.B, New func() ethdb.KeyValueStore) {
384+
var (
385+
keys, vals = makeDataset(1_000_000, 32, 32, false)
386+
sKeys, sVals = makeDataset(1_000_000, 32, 32, true)
387+
)
388+
// Run benchmarks sequentially
389+
b.Run("Write", func(b *testing.B) {
390+
benchWrite := func(b *testing.B, keys, vals [][]byte) {
391+
b.ResetTimer()
392+
b.ReportAllocs()
393+
394+
db := New()
395+
defer db.Close()
396+
397+
for i := 0; i < len(keys); i++ {
398+
db.Put(keys[i], vals[i])
399+
}
400+
}
401+
b.Run("WriteSorted", func(b *testing.B) {
402+
benchWrite(b, sKeys, sVals)
403+
})
404+
b.Run("WriteRandom", func(b *testing.B) {
405+
benchWrite(b, keys, vals)
406+
})
407+
})
408+
b.Run("Read", func(b *testing.B) {
409+
benchRead := func(b *testing.B, keys, vals [][]byte) {
410+
db := New()
411+
defer db.Close()
412+
413+
for i := 0; i < len(keys); i++ {
414+
db.Put(keys[i], vals[i])
415+
}
416+
b.ResetTimer()
417+
b.ReportAllocs()
418+
419+
for i := 0; i < len(keys); i++ {
420+
db.Get(keys[i])
421+
}
422+
}
423+
b.Run("ReadSorted", func(b *testing.B) {
424+
benchRead(b, sKeys, sVals)
425+
})
426+
b.Run("ReadRandom", func(b *testing.B) {
427+
benchRead(b, keys, vals)
428+
})
429+
})
430+
b.Run("Iteration", func(b *testing.B) {
431+
benchIteration := func(b *testing.B, keys, vals [][]byte) {
432+
db := New()
433+
defer db.Close()
434+
435+
for i := 0; i < len(keys); i++ {
436+
db.Put(keys[i], vals[i])
437+
}
438+
b.ResetTimer()
439+
b.ReportAllocs()
440+
441+
it := db.NewIterator(nil, nil)
442+
for it.Next() {
443+
}
444+
it.Release()
445+
}
446+
b.Run("IterationSorted", func(b *testing.B) {
447+
benchIteration(b, sKeys, sVals)
448+
})
449+
b.Run("IterationRandom", func(b *testing.B) {
450+
benchIteration(b, keys, vals)
451+
})
452+
})
453+
b.Run("BatchWrite", func(b *testing.B) {
454+
benchBatchWrite := func(b *testing.B, keys, vals [][]byte) {
455+
b.ResetTimer()
456+
b.ReportAllocs()
457+
458+
db := New()
459+
defer db.Close()
460+
461+
batch := db.NewBatch()
462+
for i := 0; i < len(keys); i++ {
463+
batch.Put(keys[i], vals[i])
464+
}
465+
batch.Write()
466+
}
467+
b.Run("BenchWriteSorted", func(b *testing.B) {
468+
benchBatchWrite(b, sKeys, sVals)
469+
})
470+
b.Run("BenchWriteRandom", func(b *testing.B) {
471+
benchBatchWrite(b, keys, vals)
472+
})
473+
})
474+
}
475+
380476
func iterateKeys(it ethdb.Iterator) []string {
381477
keys := []string{}
382478
for it.Next() {
@@ -386,3 +482,25 @@ func iterateKeys(it ethdb.Iterator) []string {
386482
it.Release()
387483
return keys
388484
}
485+
486+
// randomHash generates a random blob of data and returns it as a hash.
487+
func randBytes(len int) []byte {
488+
buf := make([]byte, len)
489+
if n, err := rand.Read(buf); n != len || err != nil {
490+
panic(err)
491+
}
492+
return buf
493+
}
494+
495+
func makeDataset(size, ksize, vsize int, order bool) ([][]byte, [][]byte) {
496+
var keys [][]byte
497+
var vals [][]byte
498+
for i := 0; i < size; i += 1 {
499+
keys = append(keys, randBytes(ksize))
500+
vals = append(vals, randBytes(vsize))
501+
}
502+
if order {
503+
sort.Slice(keys, func(i, j int) bool { return bytes.Compare(keys[i], keys[j]) < 0 })
504+
}
505+
return keys, vals
506+
}

ethdb/leveldb/leveldb_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,15 @@ func TestLevelDB(t *testing.T) {
3838
})
3939
})
4040
}
41+
42+
func BenchmarkLevelDB(b *testing.B) {
43+
dbtest.BenchDatabaseSuite(b, func() ethdb.KeyValueStore {
44+
db, err := leveldb.Open(storage.NewMemStorage(), nil)
45+
if err != nil {
46+
b.Fatal(err)
47+
}
48+
return &Database{
49+
db: db,
50+
}
51+
})
52+
}

ethdb/pebble/pebble.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,9 @@ func (d *Database) NewBatch() ethdb.Batch {
272272
}
273273

274274
// NewBatchWithSize creates a write-only database batch with pre-allocated buffer.
275-
// TODO can't do this with pebble. Batches are allocated in a pool so maybe this doesn't matter?
275+
// It's not supported by pebble, but pebble has better memory allocation strategy
276+
// which turns out a lot faster than leveldb. It's performant enough to construct
277+
// batch object without any pre-allocated space.
276278
func (d *Database) NewBatchWithSize(_ int) ethdb.Batch {
277279
return &batch{
278280
b: d.db.NewBatch(),

ethdb/pebble/pebble_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,17 @@ func TestPebbleDB(t *testing.T) {
4242
})
4343
})
4444
}
45+
46+
func BenchmarkPebbleDB(b *testing.B) {
47+
dbtest.BenchDatabaseSuite(b, func() ethdb.KeyValueStore {
48+
db, err := pebble.Open("", &pebble.Options{
49+
FS: vfs.NewMem(),
50+
})
51+
if err != nil {
52+
b.Fatal(err)
53+
}
54+
return &Database{
55+
db: db,
56+
}
57+
})
58+
}

0 commit comments

Comments
 (0)