]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: defer error context work until necessary
authorDaniel Martí <mvdan@mvdan.cc>
Sun, 8 Jul 2018 12:17:56 +0000 (13:17 +0100)
committerDaniel Martí <mvdan@mvdan.cc>
Tue, 21 Aug 2018 18:18:59 +0000 (18:18 +0000)
Calling Name on a reflect.Type is somewhat expensive, as it involves a
number of nested calls and string handling.

This cost was showing up when decoding structs, as we were calling it to
set up an error context.

We can avoid the extra work unless we do encounter an error, which makes
decoding via struct types faster.

name           old time/op    new time/op    delta
CodeDecoder-4    31.0ms ± 1%    29.9ms ± 1%  -3.69%  (p=0.002 n=6+6)

name           old speed      new speed      delta
CodeDecoder-4  62.6MB/s ± 1%  65.0MB/s ± 1%  +3.83%  (p=0.002 n=6+6)

Updates #5683.

Change-Id: I48a3a85ef0ba96f524b7c3e9096cb2c4589e077a
Reviewed-on: https://go-review.googlesource.com/122467
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/encoding/json/decode.go

index 97fee54f4ebfd857616ae9c0df6ca7e5edf2e354..16da48617e9ae61697b2165a88feb5c36ec7397d 100644 (file)
@@ -267,7 +267,7 @@ type decodeState struct {
        opcode       int // last read result
        scan         scanner
        errorContext struct { // provides context for type errors
-               Struct string
+               Struct reflect.Type
                Field  string
        }
        savedError            error
@@ -289,7 +289,7 @@ func (d *decodeState) init(data []byte) *decodeState {
        d.data = data
        d.off = 0
        d.savedError = nil
-       d.errorContext.Struct = ""
+       d.errorContext.Struct = nil
        d.errorContext.Field = ""
        return d
 }
@@ -304,10 +304,10 @@ func (d *decodeState) saveError(err error) {
 
 // addErrorContext returns a new error enhanced with information from d.errorContext
 func (d *decodeState) addErrorContext(err error) error {
-       if d.errorContext.Struct != "" || d.errorContext.Field != "" {
+       if d.errorContext.Struct != nil || d.errorContext.Field != "" {
                switch err := err.(type) {
                case *UnmarshalTypeError:
-                       err.Struct = d.errorContext.Struct
+                       err.Struct = d.errorContext.Struct.Name()
                        err.Field = d.errorContext.Field
                        return err
                }
@@ -744,7 +744,7 @@ func (d *decodeState) object(v reflect.Value) error {
                                        subv = subv.Field(i)
                                }
                                d.errorContext.Field = f.name
-                               d.errorContext.Struct = v.Type().Name()
+                               d.errorContext.Struct = v.Type()
                        } else if d.disallowUnknownFields {
                                d.saveError(fmt.Errorf("json: unknown field %q", key))
                        }
@@ -832,7 +832,7 @@ func (d *decodeState) object(v reflect.Value) error {
                        return errPhase
                }
 
-               d.errorContext.Struct = ""
+               d.errorContext.Struct = nil
                d.errorContext.Field = ""
        }
        return nil