mirror of
https://github.com/golang/go
synced 2024-11-19 07:24:42 -07:00
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>
This commit is contained in:
parent
e496120891
commit
04edf418d2
@ -200,16 +200,19 @@ func (n Number) Int64() (int64, error) {
|
|||||||
return strconv.ParseInt(string(n), 10, 64)
|
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.
|
// decodeState represents the state while decoding a JSON value.
|
||||||
type decodeState struct {
|
type decodeState struct {
|
||||||
data []byte
|
data []byte
|
||||||
off int // next read offset in data
|
off int // next read offset in data
|
||||||
opcode int // last read result
|
opcode int // last read result
|
||||||
scan scanner
|
scan scanner
|
||||||
errorContext struct { // provides context for type errors
|
errorContext *errorContext
|
||||||
Struct reflect.Type
|
|
||||||
FieldStack []string
|
|
||||||
}
|
|
||||||
savedError error
|
savedError error
|
||||||
useNumber bool
|
useNumber bool
|
||||||
disallowUnknownFields bool
|
disallowUnknownFields bool
|
||||||
@ -229,10 +232,11 @@ func (d *decodeState) init(data []byte) *decodeState {
|
|||||||
d.data = data
|
d.data = data
|
||||||
d.off = 0
|
d.off = 0
|
||||||
d.savedError = nil
|
d.savedError = nil
|
||||||
|
if d.errorContext != nil {
|
||||||
d.errorContext.Struct = nil
|
d.errorContext.Struct = nil
|
||||||
|
|
||||||
// Reuse the allocated space for the FieldStack slice.
|
// Reuse the allocated space for the FieldStack slice.
|
||||||
d.errorContext.FieldStack = d.errorContext.FieldStack[:0]
|
d.errorContext.FieldStack = d.errorContext.FieldStack[:0]
|
||||||
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,12 +250,11 @@ func (d *decodeState) saveError(err error) {
|
|||||||
|
|
||||||
// addErrorContext returns a new error enhanced with information from d.errorContext
|
// addErrorContext returns a new error enhanced with information from d.errorContext
|
||||||
func (d *decodeState) addErrorContext(err error) error {
|
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) {
|
switch err := err.(type) {
|
||||||
case *UnmarshalTypeError:
|
case *UnmarshalTypeError:
|
||||||
err.Struct = d.errorContext.Struct.Name()
|
err.Struct = d.errorContext.Struct.Name()
|
||||||
err.Field = strings.Join(d.errorContext.FieldStack, ".")
|
err.Field = strings.Join(d.errorContext.FieldStack, ".")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -657,7 +660,10 @@ func (d *decodeState) object(v reflect.Value) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var mapElem reflect.Value
|
var mapElem reflect.Value
|
||||||
origErrorContext := d.errorContext
|
var origErrorContext errorContext
|
||||||
|
if d.errorContext != nil {
|
||||||
|
origErrorContext = *d.errorContext
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Read opening " of string key or closing }.
|
// Read opening " of string key or closing }.
|
||||||
@ -732,6 +738,9 @@ func (d *decodeState) object(v reflect.Value) error {
|
|||||||
}
|
}
|
||||||
subv = subv.Field(i)
|
subv = subv.Field(i)
|
||||||
}
|
}
|
||||||
|
if d.errorContext == nil {
|
||||||
|
d.errorContext = new(errorContext)
|
||||||
|
}
|
||||||
d.errorContext.FieldStack = append(d.errorContext.FieldStack, f.name)
|
d.errorContext.FieldStack = append(d.errorContext.FieldStack, f.name)
|
||||||
d.errorContext.Struct = t
|
d.errorContext.Struct = t
|
||||||
} else if d.disallowUnknownFields {
|
} else if d.disallowUnknownFields {
|
||||||
@ -812,11 +821,13 @@ func (d *decodeState) object(v reflect.Value) error {
|
|||||||
if d.opcode == scanSkipSpace {
|
if d.opcode == scanSkipSpace {
|
||||||
d.scanWhile(scanSkipSpace)
|
d.scanWhile(scanSkipSpace)
|
||||||
}
|
}
|
||||||
|
if d.errorContext != nil {
|
||||||
// Reset errorContext to its original state.
|
// Reset errorContext to its original state.
|
||||||
// Keep the same underlying array for FieldStack, to reuse the
|
// Keep the same underlying array for FieldStack, to reuse the
|
||||||
// space and avoid unnecessary allocs.
|
// space and avoid unnecessary allocs.
|
||||||
d.errorContext.FieldStack = d.errorContext.FieldStack[:len(origErrorContext.FieldStack)]
|
d.errorContext.FieldStack = d.errorContext.FieldStack[:len(origErrorContext.FieldStack)]
|
||||||
d.errorContext.Struct = origErrorContext.Struct
|
d.errorContext.Struct = origErrorContext.Struct
|
||||||
|
}
|
||||||
if d.opcode == scanEndObject {
|
if d.opcode == scanEndObject {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user