]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: simplify the structEncoder type
authorDaniel Martí <mvdan@mvdan.cc>
Sun, 22 Jul 2018 11:26:38 +0000 (12:26 +0100)
committerDaniel Martí <mvdan@mvdan.cc>
Wed, 22 Aug 2018 15:08:21 +0000 (15:08 +0000)
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í <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/encoding/json/encode.go

index d5fe4d6b78b232c6930b4b2c867b182fc85a029d..40bc060644ff2e5224d798a1ba7b9a554f9c1345 100644 (file)
@@ -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
 }