mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2025-02-18 01:15:40 +00:00
Merge pull request #2057 from nspcc-dev/optimize-storage-find
Optimize `storageFind`
This commit is contained in:
commit
2b7abd20e7
4 changed files with 56 additions and 15 deletions
|
@ -41,6 +41,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zaptest"
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,22 +75,22 @@ func newTestChainWithNS(t *testing.T) (*Blockchain, util.Uint160) {
|
||||||
|
|
||||||
// newTestChain should be called before newBlock invocation to properly setup
|
// newTestChain should be called before newBlock invocation to properly setup
|
||||||
// global state.
|
// global state.
|
||||||
func newTestChain(t *testing.T) *Blockchain {
|
func newTestChain(t testing.TB) *Blockchain {
|
||||||
return newTestChainWithCustomCfg(t, nil)
|
return newTestChainWithCustomCfg(t, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestChainWithCustomCfg(t *testing.T, f func(*config.Config)) *Blockchain {
|
func newTestChainWithCustomCfg(t testing.TB, f func(*config.Config)) *Blockchain {
|
||||||
return newTestChainWithCustomCfgAndStore(t, nil, f)
|
return newTestChainWithCustomCfgAndStore(t, nil, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestChainWithCustomCfgAndStore(t *testing.T, st storage.Store, f func(*config.Config)) *Blockchain {
|
func newTestChainWithCustomCfgAndStore(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain {
|
||||||
chain := initTestChain(t, st, f)
|
chain := initTestChain(t, st, f)
|
||||||
go chain.Run()
|
go chain.Run()
|
||||||
t.Cleanup(chain.Close)
|
t.Cleanup(chain.Close)
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTestChain(t *testing.T, st storage.Store, f func(*config.Config)) *Blockchain {
|
func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain {
|
||||||
unitTestNetCfg, err := config.Load("../../config", testchain.Network())
|
unitTestNetCfg, err := config.Load("../../config", testchain.Network())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
if f != nil {
|
if f != nil {
|
||||||
|
@ -98,7 +99,11 @@ func initTestChain(t *testing.T, st storage.Store, f func(*config.Config)) *Bloc
|
||||||
if st == nil {
|
if st == nil {
|
||||||
st = storage.NewMemoryStore()
|
st = storage.NewMemoryStore()
|
||||||
}
|
}
|
||||||
chain, err := NewBlockchain(st, unitTestNetCfg.ProtocolConfiguration, zaptest.NewLogger(t))
|
log := zaptest.NewLogger(t)
|
||||||
|
if _, ok := t.(*testing.B); ok {
|
||||||
|
log = zap.NewNop()
|
||||||
|
}
|
||||||
|
chain, err := NewBlockchain(st, unitTestNetCfg.ProtocolConfiguration, log)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,18 +192,23 @@ func storageFind(ic *interop.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredMap := stackitem.NewMap()
|
arr := make([]stackitem.MapElement, 0, len(siMap))
|
||||||
for k, v := range siMap {
|
for k, v := range siMap {
|
||||||
key := append(prefix, []byte(k)...)
|
keycopy := make([]byte, len(k)+len(prefix))
|
||||||
keycopy := make([]byte, len(key))
|
copy(keycopy, prefix)
|
||||||
copy(keycopy, key)
|
copy(keycopy[len(prefix):], k)
|
||||||
filteredMap.Add(stackitem.NewByteArray(keycopy), stackitem.NewByteArray(v))
|
arr = append(arr, stackitem.MapElement{
|
||||||
|
Key: stackitem.NewByteArray(keycopy),
|
||||||
|
Value: stackitem.NewByteArray(v),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
sort.Slice(filteredMap.Value().([]stackitem.MapElement), func(i, j int) bool {
|
sort.Slice(arr, func(i, j int) bool {
|
||||||
return bytes.Compare(filteredMap.Value().([]stackitem.MapElement)[i].Key.Value().([]byte),
|
k1 := arr[i].Key.Value().([]byte)
|
||||||
filteredMap.Value().([]stackitem.MapElement)[j].Key.Value().([]byte)) == -1
|
k2 := arr[j].Key.Value().([]byte)
|
||||||
|
return bytes.Compare(k1, k2) == -1
|
||||||
})
|
})
|
||||||
|
|
||||||
|
filteredMap := stackitem.NewMapWithValue(arr)
|
||||||
item := istorage.NewIterator(filteredMap, len(prefix), opts)
|
item := istorage.NewIterator(filteredMap, len(prefix), opts)
|
||||||
ic.VM.Estack().PushVal(stackitem.NewInterop(item))
|
ic.VM.Estack().PushVal(stackitem.NewInterop(item))
|
||||||
|
|
||||||
|
|
|
@ -259,6 +259,36 @@ func TestStorageDelete(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkStorageFind(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))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
b.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStorageFind(t *testing.T) {
|
func TestStorageFind(t *testing.T) {
|
||||||
v, contractState, context, chain := createVMAndContractState(t)
|
v, contractState, context, chain := createVMAndContractState(t)
|
||||||
|
|
||||||
|
@ -451,7 +481,7 @@ func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) {
|
||||||
return v, context, chain
|
return v, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) {
|
func createVMAndContractState(t testing.TB) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) {
|
||||||
script := []byte("testscript")
|
script := []byte("testscript")
|
||||||
m := manifest.NewManifest("Test")
|
m := manifest.NewManifest("Test")
|
||||||
ne, err := nef.NewFile(script)
|
ne, err := nef.NewFile(script)
|
||||||
|
|
|
@ -121,8 +121,9 @@ func (s *MemoryStore) SeekAll(key []byte, f func(k, v []byte)) {
|
||||||
|
|
||||||
// seek is an internal unlocked implementation of Seek.
|
// seek is an internal unlocked implementation of Seek.
|
||||||
func (s *MemoryStore) seek(key []byte, f func(k, v []byte)) {
|
func (s *MemoryStore) seek(key []byte, f func(k, v []byte)) {
|
||||||
|
sk := string(key)
|
||||||
for k, v := range s.mem {
|
for k, v := range s.mem {
|
||||||
if strings.HasPrefix(k, string(key)) {
|
if strings.HasPrefix(k, sk) {
|
||||||
f([]byte(k), v)
|
f([]byte(k), v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue