From 3eed9d06f8e0bc2af8aa0b982511a24250dd0d77 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 30 Nov 2021 19:51:52 +0300 Subject: [PATCH] stackitem: add some hint to 'seen' maps It's empirical, we usually have one container, but four is likely to fit most of regular cases. --- pkg/vm/stackitem/item.go | 2 +- pkg/vm/stackitem/json.go | 4 ++-- pkg/vm/stackitem/serialization.go | 10 ++++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index 14ed782e8..8fccd7ca2 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -1097,7 +1097,7 @@ func (i *Buffer) Len() int { // Values of Interop items are not deeply copied. // It does preserve duplicates only for non-primitive types. func DeepCopy(item Item) Item { - seen := make(map[Item]Item) + seen := make(map[Item]Item, typicalNumOfItems) return deepCopy(item, seen) } diff --git a/pkg/vm/stackitem/json.go b/pkg/vm/stackitem/json.go index bb01076b0..9fd151d7a 100644 --- a/pkg/vm/stackitem/json.go +++ b/pkg/vm/stackitem/json.go @@ -42,7 +42,7 @@ var ErrTooDeep = errors.New("too deep") // Array, Struct -> array // Map -> map with keys as UTF-8 bytes func ToJSON(item Item) ([]byte, error) { - seen := make(map[Item]sliceNoPointer) + seen := make(map[Item]sliceNoPointer, typicalNumOfItems) return toJSON(nil, seen, item) } @@ -260,7 +260,7 @@ func (d *decoder) decodeMap() (*Map, error) { // ToJSONWithTypes serializes any stackitem to JSON in a lossless way. func ToJSONWithTypes(item Item) ([]byte, error) { - result, err := toJSONWithTypes(item, make(map[Item]bool)) + result, err := toJSONWithTypes(item, make(map[Item]bool, typicalNumOfItems)) if err != nil { return nil, err } diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index bda4fbc80..9ee70889b 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -13,6 +13,12 @@ import ( // (including itself). const MaxDeserialized = 2048 +// typicalNumOfItems is the number of items covering most serializaton needs. +// It's a hint used for map creation, so it's not limiting anything, it's just +// a microoptimization to avoid excessive reallocations. Most of the serialized +// items are structs, so there is at least one of them. +const typicalNumOfItems = 4 + // ErrRecursive is returned on attempts to serialize some recursive stack item // (like array including an item with reference to the same array). var ErrRecursive = errors.New("recursive item") @@ -40,7 +46,7 @@ type deserContext struct { func Serialize(item Item) ([]byte, error) { sc := serContext{ allowInvalid: false, - seen: make(map[Item]sliceNoPointer), + seen: make(map[Item]sliceNoPointer, typicalNumOfItems), } err := sc.serialize(item) if err != nil { @@ -69,7 +75,7 @@ func EncodeBinary(item Item, w *io.BinWriter) { func EncodeBinaryProtected(item Item, w *io.BinWriter) { sc := serContext{ allowInvalid: true, - seen: make(map[Item]sliceNoPointer), + seen: make(map[Item]sliceNoPointer, typicalNumOfItems), } err := sc.serialize(item) if err != nil {