From c9d89f6bacd66d4765cf36d2a4b121392921c5ed Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Fri, 1 Nov 2019 10:39:35 +0000 Subject: encoding/binary: cache struct sizes to speed up Read and Write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A majority of work is spent in dataSize when en/decoding the same struct over and over again. This wastes a lot of work, since the result doesn't change for a given reflect.Value. Cache the result of the function for structs, so that subsequent calls to dataSize can avoid doing work. name old time/op new time/op delta ReadStruct 1.00µs ± 1% 0.37µs ± 1% -62.99% (p=0.029 n=4+4) WriteStruct 1.00µs ± 3% 0.37µs ± 1% -62.69% (p=0.008 n=5+5) name old speed new speed delta ReadStruct 75.1MB/s ± 1% 202.9MB/s ± 1% +170.16% (p=0.029 n=4+4) WriteStruct 74.8MB/s ± 3% 200.4MB/s ± 1% +167.96% (p=0.008 n=5+5) Fixes #34471 Change-Id: Ic5d987ca95f1197415ef93643a0af6fc1224fdf0 Reviewed-on: https://go-review.googlesource.com/c/go/+/199539 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/encoding/binary/binary.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'src/encoding/binary/binary.go') diff --git a/src/encoding/binary/binary.go b/src/encoding/binary/binary.go index 8c2d1d9da4..43fa821b83 100644 --- a/src/encoding/binary/binary.go +++ b/src/encoding/binary/binary.go @@ -26,6 +26,7 @@ import ( "io" "math" "reflect" + "sync" ) // A ByteOrder specifies how to convert byte sequences into @@ -363,18 +364,32 @@ func Size(v interface{}) int { return dataSize(reflect.Indirect(reflect.ValueOf(v))) } +var structSize sync.Map // map[reflect.Type]int + // dataSize returns the number of bytes the actual data represented by v occupies in memory. // For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice // it returns the length of the slice times the element size and does not count the memory // occupied by the header. If the type of v is not acceptable, dataSize returns -1. func dataSize(v reflect.Value) int { - if v.Kind() == reflect.Slice { + switch v.Kind() { + case reflect.Slice: if s := sizeof(v.Type().Elem()); s >= 0 { return s * v.Len() } return -1 + + case reflect.Struct: + t := v.Type() + if size, ok := structSize.Load(t); ok { + return size.(int) + } + size := sizeof(t) + structSize.Store(t, size) + return size + + default: + return sizeof(v.Type()) } - return sizeof(v.Type()) } // sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable. -- cgit v1.3