Skip to content

Commit 6d4787a

Browse files
committed
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í <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 9a2a34e commit 6d4787a

File tree

2 files changed

+29
-32
lines changed

2 files changed

+29
-32
lines changed

src/encoding/json/decode.go

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -332,21 +332,20 @@ func (d *decodeState) skip() {
332332

333333
// scanNext processes the byte at d.data[d.off].
334334
func (d *decodeState) scanNext() {
335-
s, data, i := &d.scan, d.data, d.off
336-
if i < len(data) {
337-
d.opcode = s.step(s, data[i])
338-
d.off = i + 1
335+
if d.off < len(d.data) {
336+
d.opcode = d.scan.step(&d.scan, d.data[d.off])
337+
d.off++
339338
} else {
340-
d.opcode = s.eof()
341-
d.off = len(data) + 1 // mark processed EOF with len+1
339+
d.opcode = d.scan.eof()
340+
d.off = len(d.data) + 1 // mark processed EOF with len+1
342341
}
343342
}
344343

345344
// scanWhile processes bytes in d.data[d.off:] until it
346345
// receives a scan code not equal to op.
347346
func (d *decodeState) scanWhile(op int) {
348347
s, data, i := &d.scan, d.data, d.off
349-
for i < len(d.data) {
348+
for i < len(data) {
350349
newOp := s.step(s, data[i])
351350
i++
352351
if newOp != op {
@@ -356,7 +355,7 @@ func (d *decodeState) scanWhile(op int) {
356355
}
357356
}
358357

359-
d.off = len(d.data) + 1 // mark processed EOF with len+1
358+
d.off = len(data) + 1 // mark processed EOF with len+1
360359
d.opcode = d.scan.eof()
361360
}
362361

@@ -413,11 +412,7 @@ func (d *decodeState) valueQuoted() (interface{}, error) {
413412
default:
414413
return nil, errPhase
415414

416-
case scanBeginArray:
417-
d.skip()
418-
d.scanNext()
419-
420-
case scanBeginObject:
415+
case scanBeginArray, scanBeginObject:
421416
d.skip()
422417
d.scanNext()
423418

@@ -629,6 +624,7 @@ func (d *decodeState) object(v reflect.Value) error {
629624
return nil
630625
}
631626
v = pv
627+
t := v.Type()
632628

633629
// Decoding into nil interface? Switch to non-reflect code.
634630
if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
@@ -640,6 +636,8 @@ func (d *decodeState) object(v reflect.Value) error {
640636
return nil
641637
}
642638

639+
var fields []field
640+
643641
// Check type of target:
644642
// struct or
645643
// map[T1]T2 where T1 is string, an integer type,
@@ -648,14 +646,13 @@ func (d *decodeState) object(v reflect.Value) error {
648646
case reflect.Map:
649647
// Map key must either have string kind, have an integer kind,
650648
// or be an encoding.TextUnmarshaler.
651-
t := v.Type()
652649
switch t.Key().Kind() {
653650
case reflect.String,
654651
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
655652
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
656653
default:
657654
if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) {
658-
d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
655+
d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)})
659656
d.skip()
660657
return nil
661658
}
@@ -664,9 +661,10 @@ func (d *decodeState) object(v reflect.Value) error {
664661
v.Set(reflect.MakeMap(t))
665662
}
666663
case reflect.Struct:
664+
fields = cachedTypeFields(t)
667665
// ok
668666
default:
669-
d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
667+
d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)})
670668
d.skip()
671669
return nil
672670
}
@@ -698,7 +696,7 @@ func (d *decodeState) object(v reflect.Value) error {
698696
destring := false // whether the value is wrapped in a string to be decoded first
699697

700698
if v.Kind() == reflect.Map {
701-
elemType := v.Type().Elem()
699+
elemType := t.Elem()
702700
if !mapElem.IsValid() {
703701
mapElem = reflect.New(elemType).Elem()
704702
} else {
@@ -707,7 +705,6 @@ func (d *decodeState) object(v reflect.Value) error {
707705
subv = mapElem
708706
} else {
709707
var f *field
710-
fields := cachedTypeFields(v.Type())
711708
for i := range fields {
712709
ff := &fields[i]
713710
if bytes.Equal(ff.nameBytes, key) {
@@ -744,7 +741,7 @@ func (d *decodeState) object(v reflect.Value) error {
744741
subv = subv.Field(i)
745742
}
746743
d.errorContext.Field = f.name
747-
d.errorContext.Struct = v.Type()
744+
d.errorContext.Struct = t
748745
} else if d.disallowUnknownFields {
749746
d.saveError(fmt.Errorf("json: unknown field %q", key))
750747
}
@@ -785,13 +782,13 @@ func (d *decodeState) object(v reflect.Value) error {
785782
// Write value back to map;
786783
// if using struct, subv points into struct already.
787784
if v.Kind() == reflect.Map {
788-
kt := v.Type().Key()
785+
kt := t.Key()
789786
var kv reflect.Value
790787
switch {
791788
case kt.Kind() == reflect.String:
792789
kv = reflect.ValueOf(key).Convert(kt)
793790
case reflect.PtrTo(kt).Implements(textUnmarshalerType):
794-
kv = reflect.New(v.Type().Key())
791+
kv = reflect.New(kt)
795792
if err := d.literalStore(item, kv, true); err != nil {
796793
return err
797794
}

src/encoding/json/stream.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,19 @@ Input:
9696
// Look in the buffer for a new value.
9797
for i, c := range dec.buf[scanp:] {
9898
dec.scan.bytes++
99-
v := dec.scan.step(&dec.scan, c)
100-
if v == scanEnd {
99+
switch dec.scan.step(&dec.scan, c) {
100+
case scanEnd:
101101
scanp += i
102102
break Input
103-
}
104-
// scanEnd is delayed one byte.
105-
// We might block trying to get that byte from src,
106-
// so instead invent a space byte.
107-
if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd {
108-
scanp += i + 1
109-
break Input
110-
}
111-
if v == scanError {
103+
case scanEndObject, scanEndArray:
104+
// scanEnd is delayed one byte.
105+
// We might block trying to get that byte from src,
106+
// so instead invent a space byte.
107+
if stateEndValue(&dec.scan, ' ') == scanEnd {
108+
scanp += i + 1
109+
break Input
110+
}
111+
case scanError:
112112
dec.err = dec.scan.err
113113
return 0, dec.scan.err
114114
}

0 commit comments

Comments
 (0)