1
0
mirror of https://github.com/golang/go synced 2024-09-30 05:24:29 -06:00

encoding/gob: Speedup map decoding by reducing the allocations.

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 <bradfitz@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Filip Gruszczyński 2017-03-15 20:11:30 -07:00 committed by Rob Pike
parent f1e880386b
commit 0b9607d1d6
3 changed files with 39 additions and 4 deletions

View File

@ -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},

View File

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

View File

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