stackitem: cache serialized results in EncodeBinary

```
name            old time/op    new time/op    delta
EncodeBinary-8    8.39ms ± 1%    0.05ms ±21%  -99.44%  (p=0.000 n=10+9)

name            old alloc/op   new alloc/op   delta
EncodeBinary-8    2.24MB ± 0%    0.33MB ± 0%  -85.29%  (p=0.000 n=9+10)

name            old allocs/op  new allocs/op  delta
EncodeBinary-8     65.6k ± 0%      0.0k ± 0%  -99.95%  (p=0.000 n=10+10)
```

Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgeniy Stratonikov 2021-07-09 16:09:33 +03:00
parent 17a3f17c74
commit 8fdc7e32f5

View file

@ -22,14 +22,14 @@ type serContext struct {
uv [9]byte
data []byte
allowInvalid bool
seen map[Item]bool
seen map[Item]sliceNoPointer
}
// Serialize encodes given Item into the byte slice.
func Serialize(item Item) ([]byte, error) {
sc := serContext{
allowInvalid: false,
seen: make(map[Item]bool),
seen: make(map[Item]sliceNoPointer),
}
err := sc.serialize(item)
if err != nil {
@ -58,7 +58,7 @@ func EncodeBinary(item Item, w *io.BinWriter) {
func EncodeBinaryProtected(item Item, w *io.BinWriter) {
sc := serContext{
allowInvalid: true,
seen: make(map[Item]bool),
seen: make(map[Item]sliceNoPointer),
}
err := sc.serialize(item)
if err != nil {
@ -69,10 +69,18 @@ func EncodeBinaryProtected(item Item, w *io.BinWriter) {
}
func (w *serContext) serialize(item Item) error {
if w.seen[item] {
return ErrRecursive
if v, ok := w.seen[item]; ok {
if v.start == v.end {
return ErrRecursive
}
if len(w.data)+v.end-v.start > MaxSize {
return ErrTooBig
}
w.data = append(w.data, w.data[v.start:v.end]...)
return nil
}
start := len(w.data)
switch t := item.(type) {
case *ByteArray:
w.data = append(w.data, byte(ByteArrayT))
@ -103,7 +111,7 @@ func (w *serContext) serialize(item Item) error {
return fmt.Errorf("%w: Interop", ErrUnserializable)
}
case *Array, *Struct:
w.seen[item] = true
w.seen[item] = sliceNoPointer{}
_, isArray := t.(*Array)
if isArray {
@ -119,9 +127,9 @@ func (w *serContext) serialize(item Item) error {
return err
}
}
delete(w.seen, item)
w.seen[item] = sliceNoPointer{start, len(w.data)}
case *Map:
w.seen[item] = true
w.seen[item] = sliceNoPointer{}
elems := t.Value().([]MapElement)
w.data = append(w.data, byte(MapT))
@ -134,7 +142,7 @@ func (w *serContext) serialize(item Item) error {
return err
}
}
delete(w.seen, item)
w.seen[item] = sliceNoPointer{start, len(w.data)}
case Null:
w.data = append(w.data, byte(AnyT))
case nil: