core: optimize storageFind

Because `Map` stores elements in arbitrary order, addition of new
element takes linear time (`Index` iterates over all keys). Thus our
`storageFind` is actually quadratic in time. Optimize this by creating
map from sorted slice.

```
name           old time/op    new time/op    delta
StorageFind-8     157µs ± 2%     112µs ± 1%  -28.60%  (p=0.000 n=10+10)

name           old alloc/op   new alloc/op   delta
StorageFind-8    69.4kB ± 0%    60.5kB ± 0%  -12.90%  (p=0.000 n=9+10)

name           old allocs/op  new allocs/op  delta
StorageFind-8     2.21k ± 0%     2.00k ± 0%   -9.37%  (p=0.000 n=10+7)
```

Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgeniy Stratonikov 2021-07-12 14:12:00 +03:00
parent b8b272c8c4
commit eb7bd7b99b

View file

@ -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))