Commit 6d4787af authored by Daniel Martí's avatar Daniel Martí

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: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 9a2a34e1
...@@ -332,13 +332,12 @@ func (d *decodeState) skip() { ...@@ -332,13 +332,12 @@ func (d *decodeState) skip() {
// scanNext processes the byte at d.data[d.off]. // scanNext processes the byte at d.data[d.off].
func (d *decodeState) scanNext() { func (d *decodeState) scanNext() {
s, data, i := &d.scan, d.data, d.off if d.off < len(d.data) {
if i < len(data) { d.opcode = d.scan.step(&d.scan, d.data[d.off])
d.opcode = s.step(s, data[i]) d.off++
d.off = i + 1
} else { } else {
d.opcode = s.eof() d.opcode = d.scan.eof()
d.off = len(data) + 1 // mark processed EOF with len+1 d.off = len(d.data) + 1 // mark processed EOF with len+1
} }
} }
...@@ -346,7 +345,7 @@ func (d *decodeState) scanNext() { ...@@ -346,7 +345,7 @@ func (d *decodeState) scanNext() {
// receives a scan code not equal to op. // receives a scan code not equal to op.
func (d *decodeState) scanWhile(op int) { func (d *decodeState) scanWhile(op int) {
s, data, i := &d.scan, d.data, d.off s, data, i := &d.scan, d.data, d.off
for i < len(d.data) { for i < len(data) {
newOp := s.step(s, data[i]) newOp := s.step(s, data[i])
i++ i++
if newOp != op { if newOp != op {
...@@ -356,7 +355,7 @@ func (d *decodeState) scanWhile(op int) { ...@@ -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() d.opcode = d.scan.eof()
} }
...@@ -413,11 +412,7 @@ func (d *decodeState) valueQuoted() (interface{}, error) { ...@@ -413,11 +412,7 @@ func (d *decodeState) valueQuoted() (interface{}, error) {
default: default:
return nil, errPhase return nil, errPhase
case scanBeginArray: case scanBeginArray, scanBeginObject:
d.skip()
d.scanNext()
case scanBeginObject:
d.skip() d.skip()
d.scanNext() d.scanNext()
...@@ -629,6 +624,7 @@ func (d *decodeState) object(v reflect.Value) error { ...@@ -629,6 +624,7 @@ func (d *decodeState) object(v reflect.Value) error {
return nil return nil
} }
v = pv v = pv
t := v.Type()
// Decoding into nil interface? Switch to non-reflect code. // Decoding into nil interface? Switch to non-reflect code.
if v.Kind() == reflect.Interface && v.NumMethod() == 0 { if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
...@@ -640,6 +636,8 @@ func (d *decodeState) object(v reflect.Value) error { ...@@ -640,6 +636,8 @@ func (d *decodeState) object(v reflect.Value) error {
return nil return nil
} }
var fields []field
// Check type of target: // Check type of target:
// struct or // struct or
// map[T1]T2 where T1 is string, an integer type, // map[T1]T2 where T1 is string, an integer type,
...@@ -648,14 +646,13 @@ func (d *decodeState) object(v reflect.Value) error { ...@@ -648,14 +646,13 @@ func (d *decodeState) object(v reflect.Value) error {
case reflect.Map: case reflect.Map:
// Map key must either have string kind, have an integer kind, // Map key must either have string kind, have an integer kind,
// or be an encoding.TextUnmarshaler. // or be an encoding.TextUnmarshaler.
t := v.Type()
switch t.Key().Kind() { switch t.Key().Kind() {
case reflect.String, case reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
default: default:
if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { 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() d.skip()
return nil return nil
} }
...@@ -664,9 +661,10 @@ func (d *decodeState) object(v reflect.Value) error { ...@@ -664,9 +661,10 @@ func (d *decodeState) object(v reflect.Value) error {
v.Set(reflect.MakeMap(t)) v.Set(reflect.MakeMap(t))
} }
case reflect.Struct: case reflect.Struct:
fields = cachedTypeFields(t)
// ok // ok
default: 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() d.skip()
return nil return nil
} }
...@@ -698,7 +696,7 @@ func (d *decodeState) object(v reflect.Value) error { ...@@ -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 destring := false // whether the value is wrapped in a string to be decoded first
if v.Kind() == reflect.Map { if v.Kind() == reflect.Map {
elemType := v.Type().Elem() elemType := t.Elem()
if !mapElem.IsValid() { if !mapElem.IsValid() {
mapElem = reflect.New(elemType).Elem() mapElem = reflect.New(elemType).Elem()
} else { } else {
...@@ -707,7 +705,6 @@ func (d *decodeState) object(v reflect.Value) error { ...@@ -707,7 +705,6 @@ func (d *decodeState) object(v reflect.Value) error {
subv = mapElem subv = mapElem
} else { } else {
var f *field var f *field
fields := cachedTypeFields(v.Type())
for i := range fields { for i := range fields {
ff := &fields[i] ff := &fields[i]
if bytes.Equal(ff.nameBytes, key) { if bytes.Equal(ff.nameBytes, key) {
...@@ -744,7 +741,7 @@ func (d *decodeState) object(v reflect.Value) error { ...@@ -744,7 +741,7 @@ func (d *decodeState) object(v reflect.Value) error {
subv = subv.Field(i) subv = subv.Field(i)
} }
d.errorContext.Field = f.name d.errorContext.Field = f.name
d.errorContext.Struct = v.Type() d.errorContext.Struct = t
} else if d.disallowUnknownFields { } else if d.disallowUnknownFields {
d.saveError(fmt.Errorf("json: unknown field %q", key)) d.saveError(fmt.Errorf("json: unknown field %q", key))
} }
...@@ -785,13 +782,13 @@ func (d *decodeState) object(v reflect.Value) error { ...@@ -785,13 +782,13 @@ func (d *decodeState) object(v reflect.Value) error {
// Write value back to map; // Write value back to map;
// if using struct, subv points into struct already. // if using struct, subv points into struct already.
if v.Kind() == reflect.Map { if v.Kind() == reflect.Map {
kt := v.Type().Key() kt := t.Key()
var kv reflect.Value var kv reflect.Value
switch { switch {
case kt.Kind() == reflect.String: case kt.Kind() == reflect.String:
kv = reflect.ValueOf(key).Convert(kt) kv = reflect.ValueOf(key).Convert(kt)
case reflect.PtrTo(kt).Implements(textUnmarshalerType): 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 { if err := d.literalStore(item, kv, true); err != nil {
return err return err
} }
......
...@@ -96,19 +96,19 @@ Input: ...@@ -96,19 +96,19 @@ Input:
// Look in the buffer for a new value. // Look in the buffer for a new value.
for i, c := range dec.buf[scanp:] { for i, c := range dec.buf[scanp:] {
dec.scan.bytes++ dec.scan.bytes++
v := dec.scan.step(&dec.scan, c) switch dec.scan.step(&dec.scan, c) {
if v == scanEnd { case scanEnd:
scanp += i scanp += i
break Input break Input
} case scanEndObject, scanEndArray:
// scanEnd is delayed one byte. // scanEnd is delayed one byte.
// We might block trying to get that byte from src, // We might block trying to get that byte from src,
// so instead invent a space byte. // so instead invent a space byte.
if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd { if stateEndValue(&dec.scan, ' ') == scanEnd {
scanp += i + 1 scanp += i + 1
break Input break Input
} }
if v == scanError { case scanError:
dec.err = dec.scan.err dec.err = dec.scan.err
return 0, dec.scan.err return 0, dec.scan.err
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment