aboutsummaryrefslogtreecommitdiff
path: root/src/encoding
diff options
context:
space:
mode:
authorJosh Bleecher Snyder <josharian@gmail.com>2020-11-18 12:50:29 -0800
committerJosh Bleecher Snyder <josharian@gmail.com>2021-02-24 14:12:16 +0000
commit04edf418d285df410745118aae756f9e0a9a00f5 (patch)
tree6a03a63c804c33ca8184a64049ad977de8cc2bd9 /src/encoding
parente49612089196be102b4b7f86c417b8cfba2521aa (diff)
downloadgo-04edf418d285df410745118aae756f9e0a9a00f5.tar.gz
go-04edf418d285df410745118aae756f9e0a9a00f5.zip
encoding/json: reduce allocated space in Unmarshal
The decodeState type is a large part of the allocated space during Unmarshal. The errorContext field is infrequently used, and only on error. Extract it into a pointer and allocate it separate when necessary. name old time/op new time/op delta UnmarshalString-8 115ns ± 5% 114ns ± 3% ~ (p=0.170 n=15+15) UnmarshalFloat64-8 113ns ± 2% 106ns ± 1% -6.42% (p=0.000 n=15+14) UnmarshalInt64-8 93.3ns ± 1% 86.9ns ± 4% -6.89% (p=0.000 n=14+15) name old alloc/op new alloc/op delta UnmarshalString-8 192B ± 0% 160B ± 0% -16.67% (p=0.000 n=15+15) UnmarshalFloat64-8 180B ± 0% 148B ± 0% -17.78% (p=0.000 n=15+15) UnmarshalInt64-8 176B ± 0% 144B ± 0% -18.18% (p=0.000 n=15+15) name old allocs/op new allocs/op delta UnmarshalString-8 2.00 ± 0% 2.00 ± 0% ~ (all equal) UnmarshalFloat64-8 2.00 ± 0% 2.00 ± 0% ~ (all equal) UnmarshalInt64-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) Change-Id: I53f3f468e6c65f77a12e5138a2626455b197012d Reviewed-on: https://go-review.googlesource.com/c/go/+/271338 Trust: Josh Bleecher Snyder <josharian@gmail.com> Trust: Daniel Martí <mvdan@mvdan.cc> Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Diffstat (limited to 'src/encoding')
-rw-r--r--src/encoding/json/decode.go51
1 files changed, 31 insertions, 20 deletions
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index 86d8a69db7..a9917e72c7 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -200,16 +200,19 @@ func (n Number) Int64() (int64, error) {
return strconv.ParseInt(string(n), 10, 64)
}
+// An errorContext provides context for type errors during decoding.
+type errorContext struct {
+ Struct reflect.Type
+ FieldStack []string
+}
+
// decodeState represents the state while decoding a JSON value.
type decodeState struct {
- data []byte
- off int // next read offset in data
- opcode int // last read result
- scan scanner
- errorContext struct { // provides context for type errors
- Struct reflect.Type
- FieldStack []string
- }
+ data []byte
+ off int // next read offset in data
+ opcode int // last read result
+ scan scanner
+ errorContext *errorContext
savedError error
useNumber bool
disallowUnknownFields bool
@@ -229,10 +232,11 @@ func (d *decodeState) init(data []byte) *decodeState {
d.data = data
d.off = 0
d.savedError = nil
- d.errorContext.Struct = nil
-
- // Reuse the allocated space for the FieldStack slice.
- d.errorContext.FieldStack = d.errorContext.FieldStack[:0]
+ if d.errorContext != nil {
+ d.errorContext.Struct = nil
+ // Reuse the allocated space for the FieldStack slice.
+ d.errorContext.FieldStack = d.errorContext.FieldStack[:0]
+ }
return d
}
@@ -246,12 +250,11 @@ 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 != nil || len(d.errorContext.FieldStack) > 0 {
+ if d.errorContext != nil && (d.errorContext.Struct != nil || len(d.errorContext.FieldStack) > 0) {
switch err := err.(type) {
case *UnmarshalTypeError:
err.Struct = d.errorContext.Struct.Name()
err.Field = strings.Join(d.errorContext.FieldStack, ".")
- return err
}
}
return err
@@ -657,7 +660,10 @@ func (d *decodeState) object(v reflect.Value) error {
}
var mapElem reflect.Value
- origErrorContext := d.errorContext
+ var origErrorContext errorContext
+ if d.errorContext != nil {
+ origErrorContext = *d.errorContext
+ }
for {
// Read opening " of string key or closing }.
@@ -732,6 +738,9 @@ func (d *decodeState) object(v reflect.Value) error {
}
subv = subv.Field(i)
}
+ if d.errorContext == nil {
+ d.errorContext = new(errorContext)
+ }
d.errorContext.FieldStack = append(d.errorContext.FieldStack, f.name)
d.errorContext.Struct = t
} else if d.disallowUnknownFields {
@@ -812,11 +821,13 @@ func (d *decodeState) object(v reflect.Value) error {
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace)
}
- // Reset errorContext to its original state.
- // Keep the same underlying array for FieldStack, to reuse the
- // space and avoid unnecessary allocs.
- d.errorContext.FieldStack = d.errorContext.FieldStack[:len(origErrorContext.FieldStack)]
- d.errorContext.Struct = origErrorContext.Struct
+ if d.errorContext != nil {
+ // Reset errorContext to its original state.
+ // Keep the same underlying array for FieldStack, to reuse the
+ // space and avoid unnecessary allocs.
+ d.errorContext.FieldStack = d.errorContext.FieldStack[:len(origErrorContext.FieldStack)]
+ d.errorContext.Struct = origErrorContext.Struct
+ }
if d.opcode == scanEndObject {
break
}