From fbb0d1aac0d65529a475841968228f64def56fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sun, 22 Jul 2018 12:26:38 +0100 Subject: [PATCH] encoding/json: simplify the structEncoder type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit structEncoder had two slices - the list of fields, and a list containing the encoder for each field. structEncoder.encode then looped over the fields, and indexed into the second slice to grab the field encoder. However, this makes it very hard for the compiler to be able to prove that the two slices always have the same length, and that the index expression doesn't need a bounds check. Merge the two slices into one to completely remove the need for bounds checks in the hot loop. While at it, don't copy the field elements when ranging, which greatly speeds up the hot loop in structEncoder. name old time/op new time/op delta CodeEncoder-4 6.18ms ± 0% 5.56ms ± 0% -10.08% (p=0.002 n=6+6) name old speed new speed delta CodeEncoder-4 314MB/s ± 0% 349MB/s ± 0% +11.21% (p=0.002 n=6+6) name old alloc/op new alloc/op delta CodeEncoder-4 93.2kB ± 0% 62.1kB ± 0% -33.33% (p=0.002 n=6+6) Updates #5683. Change-Id: I0dd47783530f439b125e084aede09dda172eb1e8 Reviewed-on: https://go-review.googlesource.com/125416 Run-TryBot: Daniel Martí TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- encode.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/encode.go b/encode.go index d5fe4d6..40bc060 100644 --- a/encode.go +++ b/encode.go @@ -624,14 +624,14 @@ func unsupportedTypeEncoder(e *encodeState, v reflect.Value, _ encOpts) { } type structEncoder struct { - fields []field - fieldEncs []encoderFunc + fields []field } -func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { e.WriteByte('{') first := true - for i, f := range se.fields { + for i := range se.fields { + f := &se.fields[i] fv := fieldByIndex(v, f.index) if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) { continue @@ -647,20 +647,13 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { e.WriteString(f.nameNonEsc) } opts.quoted = f.quoted - se.fieldEncs[i](e, fv, opts) + f.encoder(e, fv, opts) } e.WriteByte('}') } func newStructEncoder(t reflect.Type) encoderFunc { - fields := cachedTypeFields(t) - se := &structEncoder{ - fields: fields, - fieldEncs: make([]encoderFunc, len(fields)), - } - for i, f := range fields { - se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index)) - } + se := structEncoder{fields: cachedTypeFields(t)} return se.encode } @@ -1055,6 +1048,8 @@ type field struct { typ reflect.Type omitEmpty bool quoted bool + + encoder encoderFunc } func fillField(f field) field { @@ -1254,6 +1249,10 @@ func typeFields(t reflect.Type) []field { fields = out sort.Sort(byIndex(fields)) + for i := range fields { + f := &fields[i] + f.encoder = typeEncoder(typeByIndex(t, f.index)) + } return fields }