forked from TrueCloudLab/neoneo-go
core: add benchmarks for iterator.Next, MemCached.Seek, Mem.Seek
This commit is contained in:
parent
8d8071f97e
commit
d8210c0137
8 changed files with 199 additions and 29 deletions
|
@ -2,6 +2,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -336,32 +337,98 @@ func TestStorageDelete(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkStorageFind(b *testing.B) {
|
func BenchmarkStorageFind(b *testing.B) {
|
||||||
v, contractState, context, chain := createVMAndContractState(b)
|
for count := 10; count <= 10000; count *= 10 {
|
||||||
require.NoError(b, chain.contracts.Management.PutContractState(chain.dao, contractState))
|
b.Run(fmt.Sprintf("%dElements", count), func(b *testing.B) {
|
||||||
|
v, contractState, context, chain := createVMAndContractState(b)
|
||||||
|
require.NoError(b, chain.contracts.Management.PutContractState(chain.dao, contractState))
|
||||||
|
|
||||||
const count = 100
|
items := make(map[string]state.StorageItem)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
items["abc"+random.String(10)] = random.Bytes(10)
|
||||||
|
}
|
||||||
|
for k, v := range items {
|
||||||
|
require.NoError(b, context.DAO.PutStorageItem(contractState.ID, []byte(k), v))
|
||||||
|
require.NoError(b, context.DAO.PutStorageItem(contractState.ID+1, []byte(k), v))
|
||||||
|
}
|
||||||
|
changes, err := context.DAO.Persist()
|
||||||
|
require.NoError(b, err)
|
||||||
|
require.NotEqual(b, 0, changes)
|
||||||
|
|
||||||
items := make(map[string]state.StorageItem)
|
b.ResetTimer()
|
||||||
for i := 0; i < count; i++ {
|
b.ReportAllocs()
|
||||||
items["abc"+random.String(10)] = random.Bytes(10)
|
for i := 0; i < b.N; i++ {
|
||||||
}
|
b.StopTimer()
|
||||||
for k, v := range items {
|
v.Estack().PushVal(istorage.FindDefault)
|
||||||
require.NoError(b, context.DAO.PutStorageItem(contractState.ID, []byte(k), v))
|
v.Estack().PushVal("abc")
|
||||||
require.NoError(b, context.DAO.PutStorageItem(contractState.ID+1, []byte(k), v))
|
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: contractState.ID}))
|
||||||
|
b.StartTimer()
|
||||||
|
err := storageFind(context)
|
||||||
|
if err != nil {
|
||||||
|
b.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
func BenchmarkStorageFindIteratorNext(b *testing.B) {
|
||||||
b.ReportAllocs()
|
for count := 10; count <= 10000; count *= 10 {
|
||||||
for i := 0; i < b.N; i++ {
|
cases := map[string]int{
|
||||||
b.StopTimer()
|
"Pick1": 1,
|
||||||
v.Estack().PushVal(istorage.FindDefault)
|
"PickHalf": count / 2,
|
||||||
v.Estack().PushVal("abc")
|
"PickAll": count,
|
||||||
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: contractState.ID}))
|
|
||||||
b.StartTimer()
|
|
||||||
err := storageFind(context)
|
|
||||||
if err != nil {
|
|
||||||
b.FailNow()
|
|
||||||
}
|
}
|
||||||
|
b.Run(fmt.Sprintf("%dElements", count), func(b *testing.B) {
|
||||||
|
for name, last := range cases {
|
||||||
|
b.Run(name, func(b *testing.B) {
|
||||||
|
v, contractState, context, chain := createVMAndContractState(b)
|
||||||
|
require.NoError(b, chain.contracts.Management.PutContractState(chain.dao, contractState))
|
||||||
|
|
||||||
|
items := make(map[string]state.StorageItem)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
items["abc"+random.String(10)] = random.Bytes(10)
|
||||||
|
}
|
||||||
|
for k, v := range items {
|
||||||
|
require.NoError(b, context.DAO.PutStorageItem(contractState.ID, []byte(k), v))
|
||||||
|
require.NoError(b, context.DAO.PutStorageItem(contractState.ID+1, []byte(k), v))
|
||||||
|
}
|
||||||
|
changes, err := context.DAO.Persist()
|
||||||
|
require.NoError(b, err)
|
||||||
|
require.NotEqual(b, 0, changes)
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
b.StopTimer()
|
||||||
|
v.Estack().PushVal(istorage.FindDefault)
|
||||||
|
v.Estack().PushVal("abc")
|
||||||
|
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: contractState.ID}))
|
||||||
|
b.StartTimer()
|
||||||
|
err := storageFind(context)
|
||||||
|
b.StopTimer()
|
||||||
|
if err != nil {
|
||||||
|
b.FailNow()
|
||||||
|
}
|
||||||
|
res := context.VM.Estack().Pop().Item()
|
||||||
|
for i := 0; i < last; i++ {
|
||||||
|
context.VM.Estack().PushVal(res)
|
||||||
|
b.StartTimer()
|
||||||
|
require.NoError(b, iterator.Next(context))
|
||||||
|
b.StopTimer()
|
||||||
|
require.True(b, context.VM.Estack().Pop().Bool())
|
||||||
|
}
|
||||||
|
|
||||||
|
context.VM.Estack().PushVal(res)
|
||||||
|
require.NoError(b, iterator.Next(context))
|
||||||
|
actual := context.VM.Estack().Pop().Bool()
|
||||||
|
if last == count {
|
||||||
|
require.False(b, actual)
|
||||||
|
} else {
|
||||||
|
require.True(b, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newBadgerDBForTesting(t *testing.T) Store {
|
func newBadgerDBForTesting(t testing.TB) Store {
|
||||||
bdbDir := t.TempDir()
|
bdbDir := t.TempDir()
|
||||||
dbConfig := DBConfiguration{
|
dbConfig := DBConfiguration{
|
||||||
Type: "badgerdb",
|
Type: "badgerdb",
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newBoltStoreForTesting(t *testing.T) Store {
|
func newBoltStoreForTesting(t testing.TB) Store {
|
||||||
d := t.TempDir()
|
d := t.TempDir()
|
||||||
testFileName := path.Join(d, "test_bolt_db")
|
testFileName := path.Join(d, "test_bolt_db")
|
||||||
boltDBStore, err := NewBoltDBStore(BoltDBOptions{FilePath: testFileName})
|
boltDBStore, err := NewBoltDBStore(BoltDBOptions{FilePath: testFileName})
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newLevelDBForTesting(t *testing.T) Store {
|
func newLevelDBForTesting(t testing.TB) Store {
|
||||||
ldbDir := t.TempDir()
|
ldbDir := t.TempDir()
|
||||||
dbConfig := DBConfiguration{
|
dbConfig := DBConfiguration{
|
||||||
Type: "leveldb",
|
Type: "leveldb",
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -174,7 +176,82 @@ func TestCachedSeek(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMemCachedStoreForTesting(t *testing.T) Store {
|
func benchmarkCachedSeek(t *testing.B, ps Store, psElementsCount, tsElementsCount int) {
|
||||||
|
var (
|
||||||
|
searchPrefix = []byte{1}
|
||||||
|
badPrefix = []byte{2}
|
||||||
|
lowerPrefixGood = append(searchPrefix, 1)
|
||||||
|
lowerPrefixBad = append(badPrefix, 1)
|
||||||
|
deletedPrefixGood = append(searchPrefix, 2)
|
||||||
|
deletedPrefixBad = append(badPrefix, 2)
|
||||||
|
updatedPrefixGood = append(searchPrefix, 3)
|
||||||
|
updatedPrefixBad = append(badPrefix, 3)
|
||||||
|
|
||||||
|
ts = NewMemCachedStore(ps)
|
||||||
|
)
|
||||||
|
for i := 0; i < psElementsCount; i++ {
|
||||||
|
// lower KVs with matching prefix that should be found
|
||||||
|
require.NoError(t, ps.Put(append(lowerPrefixGood, random.Bytes(10)...), []byte("value")))
|
||||||
|
// lower KVs with non-matching prefix that shouldn't be found
|
||||||
|
require.NoError(t, ps.Put(append(lowerPrefixBad, random.Bytes(10)...), []byte("value")))
|
||||||
|
|
||||||
|
// deleted KVs with matching prefix that shouldn't be found
|
||||||
|
key := append(deletedPrefixGood, random.Bytes(10)...)
|
||||||
|
require.NoError(t, ps.Put(key, []byte("deleted")))
|
||||||
|
if i < tsElementsCount {
|
||||||
|
require.NoError(t, ts.Delete(key))
|
||||||
|
}
|
||||||
|
// deleted KVs with non-matching prefix that shouldn't be found
|
||||||
|
key = append(deletedPrefixBad, random.Bytes(10)...)
|
||||||
|
require.NoError(t, ps.Put(key, []byte("deleted")))
|
||||||
|
if i < tsElementsCount {
|
||||||
|
require.NoError(t, ts.Delete(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// updated KVs with matching prefix that should be found
|
||||||
|
key = append(updatedPrefixGood, random.Bytes(10)...)
|
||||||
|
require.NoError(t, ps.Put(key, []byte("stub")))
|
||||||
|
if i < tsElementsCount {
|
||||||
|
require.NoError(t, ts.Put(key, []byte("updated")))
|
||||||
|
}
|
||||||
|
// updated KVs with non-matching prefix that shouldn't be found
|
||||||
|
key = append(updatedPrefixBad, random.Bytes(10)...)
|
||||||
|
require.NoError(t, ps.Put(key, []byte("stub")))
|
||||||
|
if i < tsElementsCount {
|
||||||
|
require.NoError(t, ts.Put(key, []byte("updated")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.ReportAllocs()
|
||||||
|
t.ResetTimer()
|
||||||
|
for n := 0; n < t.N; n++ {
|
||||||
|
ts.Seek(searchPrefix, func(k, v []byte) {})
|
||||||
|
}
|
||||||
|
t.StopTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCachedSeek(t *testing.B) {
|
||||||
|
var stores = map[string]func(testing.TB) Store{
|
||||||
|
"MemPS": func(t testing.TB) Store {
|
||||||
|
return NewMemoryStore()
|
||||||
|
},
|
||||||
|
"BoltPS": newBoltStoreForTesting,
|
||||||
|
"LevelPS": newLevelDBForTesting,
|
||||||
|
}
|
||||||
|
for psName, newPS := range stores {
|
||||||
|
for psCount := 100; psCount <= 10000; psCount *= 10 {
|
||||||
|
for tsCount := 10; tsCount <= psCount; tsCount *= 10 {
|
||||||
|
t.Run(fmt.Sprintf("%s_%dTSItems_%dPSItems", psName, tsCount, psCount), func(t *testing.B) {
|
||||||
|
ps := newPS(t)
|
||||||
|
benchmarkCachedSeek(t, ps, psCount, tsCount)
|
||||||
|
ps.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMemCachedStoreForTesting(t testing.TB) Store {
|
||||||
return NewMemCachedStore(NewMemoryStore())
|
return NewMemCachedStore(NewMemoryStore())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,35 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newMemoryStoreForTesting(t *testing.T) Store {
|
func newMemoryStoreForTesting(t testing.TB) Store {
|
||||||
return NewMemoryStore()
|
return NewMemoryStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkMemorySeek(t *testing.B) {
|
||||||
|
for count := 10; count <= 10000; count *= 10 {
|
||||||
|
t.Run(fmt.Sprintf("%dElements", count), func(t *testing.B) {
|
||||||
|
ms := NewMemoryStore()
|
||||||
|
var (
|
||||||
|
searchPrefix = []byte{1}
|
||||||
|
badPrefix = []byte{2}
|
||||||
|
)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
require.NoError(t, ms.Put(append(searchPrefix, random.Bytes(10)...), random.Bytes(10)))
|
||||||
|
require.NoError(t, ms.Put(append(badPrefix, random.Bytes(10)...), random.Bytes(10)))
|
||||||
|
}
|
||||||
|
|
||||||
|
t.ReportAllocs()
|
||||||
|
t.ResetTimer()
|
||||||
|
for n := 0; n < t.N; n++ {
|
||||||
|
ms.Seek(searchPrefix, func(k, v []byte) {})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ type mockedRedisStore struct {
|
||||||
mini *miniredis.Miniredis
|
mini *miniredis.Miniredis
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareRedisMock(t *testing.T) (*miniredis.Miniredis, *RedisStore) {
|
func prepareRedisMock(t testing.TB) (*miniredis.Miniredis, *RedisStore) {
|
||||||
miniRedis, err := miniredis.Run()
|
miniRedis, err := miniredis.Run()
|
||||||
require.Nil(t, err, "MiniRedis mock creation error")
|
require.Nil(t, err, "MiniRedis mock creation error")
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ func (mrs *mockedRedisStore) Close() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRedisStoreForTesting(t *testing.T) Store {
|
func newRedisStoreForTesting(t testing.TB) Store {
|
||||||
mock, rs := prepareRedisMock(t)
|
mock, rs := prepareRedisMock(t)
|
||||||
mrs := &mockedRedisStore{RedisStore: *rs, mini: mock}
|
mrs := &mockedRedisStore{RedisStore: *rs, mini: mock}
|
||||||
return mrs
|
return mrs
|
||||||
|
|
|
@ -19,7 +19,7 @@ type kvSeen struct {
|
||||||
|
|
||||||
type dbSetup struct {
|
type dbSetup struct {
|
||||||
name string
|
name string
|
||||||
create func(*testing.T) Store
|
create func(testing.TB) Store
|
||||||
}
|
}
|
||||||
|
|
||||||
type dbTestFunction func(*testing.T, Store)
|
type dbTestFunction func(*testing.T, Store)
|
||||||
|
|
Loading…
Reference in a new issue