Commit 81487266 authored by Daniel Martí's avatar Daniel Martí

encoding/json: simplify the structEncoder type

structEncoder had two slices - the list of fields, and a list containing
the encoder for each field. structEncoder.encode then looped over the
fields, and indexed into the second slice to grab the field encoder.

However, this makes it very hard for the compiler to be able to prove
that the two slices always have the same length, and that the index
expression doesn't need a bounds check.

Merge the two slices into one to completely remove the need for bounds
checks in the hot loop.

While at it, don't copy the field elements when ranging, which greatly
speeds up the hot loop in structEncoder.

name           old time/op    new time/op    delta
CodeEncoder-4    6.18ms ± 0%    5.56ms ± 0%  -10.08%  (p=0.002 n=6+6)

name           old speed      new speed      delta
CodeEncoder-4   314MB/s ± 0%   349MB/s ± 0%  +11.21%  (p=0.002 n=6+6)

name           old alloc/op   new alloc/op   delta
CodeEncoder-4    93.2kB ± 0%    62.1kB ± 0%  -33.33%  (p=0.002 n=6+6)

Updates #5683.

Change-Id: I0dd47783530f439b125e084aede09dda172eb1e8
Reviewed-on: https://go-review.googlesource.com/125416
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent f4e4ec2c
...@@ -624,14 +624,14 @@ func unsupportedTypeEncoder(e *encodeState, v reflect.Value, _ encOpts) { ...@@ -624,14 +624,14 @@ func unsupportedTypeEncoder(e *encodeState, v reflect.Value, _ encOpts) {
} }
type structEncoder struct { type structEncoder struct {
fields []field fields []field
fieldEncs []encoderFunc
} }
func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.WriteByte('{') e.WriteByte('{')
first := true first := true
for i, f := range se.fields { for i := range se.fields {
f := &se.fields[i]
fv := fieldByIndex(v, f.index) fv := fieldByIndex(v, f.index)
if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) { if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
continue continue
...@@ -647,20 +647,13 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { ...@@ -647,20 +647,13 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.WriteString(f.nameNonEsc) e.WriteString(f.nameNonEsc)
} }
opts.quoted = f.quoted opts.quoted = f.quoted
se.fieldEncs[i](e, fv, opts) f.encoder(e, fv, opts)
} }
e.WriteByte('}') e.WriteByte('}')
} }
func newStructEncoder(t reflect.Type) encoderFunc { func newStructEncoder(t reflect.Type) encoderFunc {
fields := cachedTypeFields(t) se := structEncoder{fields: cachedTypeFields(t)}
se := &structEncoder{
fields: fields,
fieldEncs: make([]encoderFunc, len(fields)),
}
for i, f := range fields {
se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index))
}
return se.encode return se.encode
} }
...@@ -1055,6 +1048,8 @@ type field struct { ...@@ -1055,6 +1048,8 @@ type field struct {
typ reflect.Type typ reflect.Type
omitEmpty bool omitEmpty bool
quoted bool quoted bool
encoder encoderFunc
} }
func fillField(f field) field { func fillField(f field) field {
...@@ -1254,6 +1249,10 @@ func typeFields(t reflect.Type) []field { ...@@ -1254,6 +1249,10 @@ func typeFields(t reflect.Type) []field {
fields = out fields = out
sort.Sort(byIndex(fields)) sort.Sort(byIndex(fields))
for i := range fields {
f := &fields[i]
f.encoder = typeEncoder(typeByIndex(t, f.index))
}
return fields return fields
} }
......
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