aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/json/decode.go
diff options
context:
space:
mode:
authorDaniel Martí <mvdan@mvdan.cc>2018-07-08 16:14:35 +0100
committerDaniel Martí <mvdan@mvdan.cc>2018-08-21 19:00:08 +0000
commit6d4787aff205c242895bb072a18f9066a00d00b3 (patch)
treeeb2804d4e598ab1c43fd0c6d8fe65bc14f052830 /src/encoding/json/decode.go
parent9a2a34e1c1be53fce0782a0b5e14e6f7ceaad62a (diff)
downloadgo-6d4787aff205c242895bb072a18f9066a00d00b3.tar.xz
encoding/json: various minor decoder speed-ups
Reuse v.Type() and cachedTypeFields(t) when decoding maps and structs. Always use the same data slices when in hot loops, to ensure that the compiler generates good code. "for i < len(data) { use(d.data[i]) }" makes it harder for the compiler. Finally, do other minor clean-ups, such as deduplicating switch cases, and using a switch instead of three chained ifs. The decoder sees a noticeable speed-up, in particular when decoding structs. name old time/op new time/op delta CodeDecoder-4 29.8ms ± 1% 27.5ms ± 0% -7.83% (p=0.002 n=6+6) name old speed new speed delta CodeDecoder-4 65.0MB/s ± 1% 70.6MB/s ± 0% +8.49% (p=0.002 n=6+6) Updates #5683. Change-Id: I9d751e22502221962da696e48996ffdeb777277d Reviewed-on: https://go-review.googlesource.com/122468 Run-TryBot: Daniel Martí <mvdan@mvdan.cc> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/encoding/json/decode.go')
-rw-r--r--src/encoding/json/decode.go39
1 files changed, 18 insertions, 21 deletions
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index 16da48617e..2e734fb39e 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -332,13 +332,12 @@ func (d *decodeState) skip() {
// scanNext processes the byte at d.data[d.off].
func (d *decodeState) scanNext() {
- s, data, i := &d.scan, d.data, d.off
- if i < len(data) {
- d.opcode = s.step(s, data[i])
- d.off = i + 1
+ if d.off < len(d.data) {
+ d.opcode = d.scan.step(&d.scan, d.data[d.off])
+ d.off++
} else {
- d.opcode = s.eof()
- d.off = len(data) + 1 // mark processed EOF with len+1
+ d.opcode = d.scan.eof()
+ d.off = len(d.data) + 1 // mark processed EOF with len+1
}
}
@@ -346,7 +345,7 @@ func (d *decodeState) scanNext() {
// receives a scan code not equal to op.
func (d *decodeState) scanWhile(op int) {
s, data, i := &d.scan, d.data, d.off
- for i < len(d.data) {
+ for i < len(data) {
newOp := s.step(s, data[i])
i++
if newOp != op {
@@ -356,7 +355,7 @@ func (d *decodeState) scanWhile(op int) {
}
}
- d.off = len(d.data) + 1 // mark processed EOF with len+1
+ d.off = len(data) + 1 // mark processed EOF with len+1
d.opcode = d.scan.eof()
}
@@ -413,11 +412,7 @@ func (d *decodeState) valueQuoted() (interface{}, error) {
default:
return nil, errPhase
- case scanBeginArray:
- d.skip()
- d.scanNext()
-
- case scanBeginObject:
+ case scanBeginArray, scanBeginObject:
d.skip()
d.scanNext()
@@ -629,6 +624,7 @@ func (d *decodeState) object(v reflect.Value) error {
return nil
}
v = pv
+ t := v.Type()
// Decoding into nil interface? Switch to non-reflect code.
if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
@@ -640,6 +636,8 @@ func (d *decodeState) object(v reflect.Value) error {
return nil
}
+ var fields []field
+
// Check type of target:
// struct or
// map[T1]T2 where T1 is string, an integer type,
@@ -648,14 +646,13 @@ func (d *decodeState) object(v reflect.Value) error {
case reflect.Map:
// Map key must either have string kind, have an integer kind,
// or be an encoding.TextUnmarshaler.
- t := v.Type()
switch t.Key().Kind() {
case reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
default:
if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) {
- d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)})
d.skip()
return nil
}
@@ -664,9 +661,10 @@ func (d *decodeState) object(v reflect.Value) error {
v.Set(reflect.MakeMap(t))
}
case reflect.Struct:
+ fields = cachedTypeFields(t)
// ok
default:
- d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)})
d.skip()
return nil
}
@@ -698,7 +696,7 @@ func (d *decodeState) object(v reflect.Value) error {
destring := false // whether the value is wrapped in a string to be decoded first
if v.Kind() == reflect.Map {
- elemType := v.Type().Elem()
+ elemType := t.Elem()
if !mapElem.IsValid() {
mapElem = reflect.New(elemType).Elem()
} else {
@@ -707,7 +705,6 @@ func (d *decodeState) object(v reflect.Value) error {
subv = mapElem
} else {
var f *field
- fields := cachedTypeFields(v.Type())
for i := range fields {
ff := &fields[i]
if bytes.Equal(ff.nameBytes, key) {
@@ -744,7 +741,7 @@ func (d *decodeState) object(v reflect.Value) error {
subv = subv.Field(i)
}
d.errorContext.Field = f.name
- d.errorContext.Struct = v.Type()
+ d.errorContext.Struct = t
} else if d.disallowUnknownFields {
d.saveError(fmt.Errorf("json: unknown field %q", key))
}
@@ -785,13 +782,13 @@ func (d *decodeState) object(v reflect.Value) error {
// Write value back to map;
// if using struct, subv points into struct already.
if v.Kind() == reflect.Map {
- kt := v.Type().Key()
+ kt := t.Key()
var kv reflect.Value
switch {
case kt.Kind() == reflect.String:
kv = reflect.ValueOf(key).Convert(kt)
case reflect.PtrTo(kt).Implements(textUnmarshalerType):
- kv = reflect.New(v.Type().Key())
+ kv = reflect.New(kt)
if err := d.literalStore(item, kv, true); err != nil {
return err
}