From 0b9607d1d648ae77d2db86a991db4a1fe921dbd8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Filip=20Gruszczy=C5=84ski?= Date: Wed, 15 Mar 2017 20:11:30 -0700 Subject: [PATCH] encoding/gob: Speedup map decoding by reducing the allocations. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The improvementis achieved in encoding/gob/decode.go decodeMap by allocate keyInstr and elemInstr only once and pass it to decodeIntoValue, instead of allocating a new instance on every loop cycle. name old time/op new time/op delta DecodeComplex128Slice-8 64.2µs ±10% 62.2µs ± 8% ~ (p=0.686 n=4+4) DecodeFloat64Slice-8 37.1µs ± 3% 36.5µs ± 5% ~ (p=0.343 n=4+4) DecodeInt32Slice-8 33.7µs ± 3% 32.7µs ± 4% ~ (p=0.200 n=4+4) DecodeStringSlice-8 59.7µs ± 5% 57.3µs ± 1% ~ (p=0.114 n=4+4) DecodeInterfaceSlice-8 543µs ± 7% 497µs ± 3% ~ (p=0.057 n=4+4) DecodeMap-8 3.78ms ± 8% 2.66ms ± 2% -29.69% (p=0.029 n=4+4) Updates #19525 Change-Id: Iec5fa4530de76f0a70da5de8a129a567b4aa096e Reviewed-on: https://go-review.googlesource.com/38317 Reviewed-by: Brad Fitzpatrick Reviewed-by: Rob Pike Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/encoding/gob/codec_test.go | 8 ++++++++ src/encoding/gob/decode.go | 10 ++++++---- src/encoding/gob/timing_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/encoding/gob/codec_test.go b/src/encoding/gob/codec_test.go index d4002cbcca..31c6aa6dd7 100644 --- a/src/encoding/gob/codec_test.go +++ b/src/encoding/gob/codec_test.go @@ -545,11 +545,16 @@ func TestEndToEnd(t *testing.T) { type T2 struct { T string } + type T3 struct { + X float64 + Z *int + } s1 := "string1" s2 := "string2" type T1 struct { A, B, C int M map[string]*float64 + M2 map[int]T3 EmptyMap map[string]int // to check that we receive a non-nil map. N *[3]float64 Strs *[2]string @@ -561,11 +566,14 @@ func TestEndToEnd(t *testing.T) { } pi := 3.14159 e := 2.71828 + meaning := 42 + fingers := 5 t1 := &T1{ A: 17, B: 18, C: -5, M: map[string]*float64{"pi": &pi, "e": &e}, + M2: map[int]T3{4: T3{X: pi, Z: &meaning}, 10: T3{X: e, Z: &fingers}}, EmptyMap: make(map[string]int), N: &[3]float64{1.5, 2.5, 3.5}, Strs: &[2]string{s1, s2}, diff --git a/src/encoding/gob/decode.go b/src/encoding/gob/decode.go index 92d9d3ef87..645aa71c38 100644 --- a/src/encoding/gob/decode.go +++ b/src/encoding/gob/decode.go @@ -542,12 +542,12 @@ func (dec *Decoder) decodeArray(state *decoderState, value reflect.Value, elemOp } // decodeIntoValue is a helper for map decoding. -func decodeIntoValue(state *decoderState, op decOp, isPtr bool, value reflect.Value, ovfl error) reflect.Value { - instr := &decInstr{op, 0, nil, ovfl} +func decodeIntoValue(state *decoderState, op decOp, isPtr bool, value reflect.Value, instr *decInstr) reflect.Value { v := value if isPtr { v = decAlloc(value) } + op(instr, state, v) return value } @@ -564,9 +564,11 @@ func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, value refl n := int(state.decodeUint()) keyIsPtr := mtyp.Key().Kind() == reflect.Ptr elemIsPtr := mtyp.Elem().Kind() == reflect.Ptr + keyInstr := &decInstr{keyOp, 0, nil, ovfl} + elemInstr := &decInstr{elemOp, 0, nil, ovfl} for i := 0; i < n; i++ { - key := decodeIntoValue(state, keyOp, keyIsPtr, allocValue(mtyp.Key()), ovfl) - elem := decodeIntoValue(state, elemOp, elemIsPtr, allocValue(mtyp.Elem()), ovfl) + key := decodeIntoValue(state, keyOp, keyIsPtr, allocValue(mtyp.Key()), keyInstr) + elem := decodeIntoValue(state, elemOp, elemIsPtr, allocValue(mtyp.Elem()), elemInstr) value.SetMapIndex(key, elem) } } diff --git a/src/encoding/gob/timing_test.go b/src/encoding/gob/timing_test.go index a7e7e683cc..e3578992fc 100644 --- a/src/encoding/gob/timing_test.go +++ b/src/encoding/gob/timing_test.go @@ -364,3 +364,28 @@ func BenchmarkDecodeInterfaceSlice(b *testing.B) { } } } + +func BenchmarkDecodeMap(b *testing.B) { + count := 10000 + m := make(map[int]int, count) + for i := 0; i < count; i++ { + m[i] = i + } + var buf bytes.Buffer + enc := NewEncoder(&buf) + err := enc.Encode(m) + if err != nil { + b.Fatal(err) + } + bbuf := benchmarkBuf{data: buf.Bytes()} + b.ResetTimer() + for i := 0; i < b.N; i++ { + rm := make(map[int]int, 0) + bbuf.reset() + dec := NewDecoder(&bbuf) + err := dec.Decode(&rm) + if err != nil { + b.Fatal(i, err) + } + } +} -- 2.48.1