cosmos-sdk/store/cachekv/store_bench_test.go

145 lines
3.9 KiB
Go

package cachekv_test
import (
"testing"
dbm "github.com/tendermint/tm-db"
"github.com/cosmos/cosmos-sdk/store/cachekv"
"github.com/cosmos/cosmos-sdk/store/dbadapter"
)
var sink interface{}
const defaultValueSizeBz = 1 << 12
// This benchmark measures the time of iterator.Next() when the parent store is blank
func benchmarkBlankParentIteratorNext(b *testing.B, keysize int) {
mem := dbadapter.Store{DB: dbm.NewMemDB()}
kvstore := cachekv.NewStore(mem)
// Use a singleton for value, to not waste time computing it
value := randSlice(defaultValueSizeBz)
// Use simple values for keys, pick a random start,
// and take next b.N keys sequentially after.]
startKey := randSlice(32)
// Add 1 to avoid issues when b.N = 1
keys := generateSequentialKeys(startKey, b.N+1)
for _, k := range keys {
kvstore.Set(k, value)
}
b.ReportAllocs()
b.ResetTimer()
iter := kvstore.Iterator(keys[0], keys[b.N])
defer iter.Close()
for _ = iter.Key(); iter.Valid(); iter.Next() {
// deadcode elimination stub
sink = iter
}
}
// Benchmark setting New keys to a store, where the new keys are in sequence.
func benchmarkBlankParentAppend(b *testing.B, keysize int) {
mem := dbadapter.Store{DB: dbm.NewMemDB()}
kvstore := cachekv.NewStore(mem)
// Use a singleton for value, to not waste time computing it
value := randSlice(32)
// Use simple values for keys, pick a random start,
// and take next b.N keys sequentially after.
startKey := randSlice(32)
keys := generateSequentialKeys(startKey, b.N)
b.ReportAllocs()
b.ResetTimer()
for _, k := range keys {
kvstore.Set(k, value)
}
}
// Benchmark setting New keys to a store, where the new keys are random.
// the speed of this function does not depend on the values in the parent store
func benchmarkRandomSet(b *testing.B, keysize int) {
mem := dbadapter.Store{DB: dbm.NewMemDB()}
kvstore := cachekv.NewStore(mem)
// Use a singleton for value, to not waste time computing it
value := randSlice(defaultValueSizeBz)
keys := generateRandomKeys(keysize, b.N)
b.ReportAllocs()
b.ResetTimer()
for _, k := range keys {
kvstore.Set(k, value)
}
iter := kvstore.Iterator(keys[0], keys[b.N])
defer iter.Close()
for _ = iter.Key(); iter.Valid(); iter.Next() {
// deadcode elimination stub
sink = iter
}
}
// Benchmark creating an iterator on a parent with D entries,
// that are all deleted in the cacheKV store.
// We essentially are benchmarking the cacheKV iterator creation & iteration times
// with the number of entries deleted in the parent.
func benchmarkIteratorOnParentWithManyDeletes(b *testing.B, numDeletes int) {
mem := dbadapter.Store{DB: dbm.NewMemDB()}
// Use a singleton for value, to not waste time computing it
value := randSlice(32)
// Use simple values for keys, pick a random start,
// and take next D keys sequentially after.
startKey := randSlice(32)
keys := generateSequentialKeys(startKey, numDeletes)
// setup parent db with D keys.
for _, k := range keys {
mem.Set(k, value)
}
kvstore := cachekv.NewStore(mem)
// Delete all keys from the cache KV store.
// The keys[1:] is to keep at least one entry in parent, due to a bug in the SDK iterator design.
// Essentially the iterator will never be valid, in that it should never run.
// However, this is incompatible with the for loop structure the SDK uses, hence
// causes a panic. Thus we do keys[1:].
for _, k := range keys[1:] {
kvstore.Delete(k)
}
b.ReportAllocs()
b.ResetTimer()
iter := kvstore.Iterator(keys[0], keys[b.N])
defer iter.Close()
for _ = iter.Key(); iter.Valid(); iter.Next() {
// deadcode elimination stub
sink = iter
}
}
func BenchmarkBlankParentIteratorNextKeySize32(b *testing.B) {
benchmarkBlankParentIteratorNext(b, 32)
}
func BenchmarkBlankParentAppendKeySize32(b *testing.B) {
benchmarkBlankParentAppend(b, 32)
}
func BenchmarkSetKeySize32(b *testing.B) {
benchmarkRandomSet(b, 32)
}
func BenchmarkIteratorOnParentWith1MDeletes(b *testing.B) {
benchmarkIteratorOnParentWithManyDeletes(b, 1_000_000)
}