Skip to content

Commit

Permalink
encoding/json: simplify the structEncoder type
Browse files Browse the repository at this point in the history
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: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information
mvdan committed Aug 22, 2018
1 parent 792d11c commit fbb0d1a
Showing 1 changed file with 12 additions and 13 deletions.
25 changes: 12 additions & 13 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,14 +624,14 @@ func unsupportedTypeEncoder(e *encodeState, v reflect.Value, _ encOpts) {
}

type structEncoder struct {
fields []field
fieldEncs []encoderFunc
fields []field
}

func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.WriteByte('{')
first := true
for i, f := range se.fields {
for i := range se.fields {
f := &se.fields[i]
fv := fieldByIndex(v, f.index)
if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
continue
Expand All @@ -647,20 +647,13 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.WriteString(f.nameNonEsc)
}
opts.quoted = f.quoted
se.fieldEncs[i](e, fv, opts)
f.encoder(e, fv, opts)
}
e.WriteByte('}')
}

func newStructEncoder(t reflect.Type) encoderFunc {
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))
}
se := structEncoder{fields: cachedTypeFields(t)}
return se.encode
}

Expand Down Expand Up @@ -1055,6 +1048,8 @@ type field struct {
typ reflect.Type
omitEmpty bool
quoted bool

encoder encoderFunc
}

func fillField(f field) field {
Expand Down Expand Up @@ -1254,6 +1249,10 @@ func typeFields(t reflect.Type) []field {
fields = out
sort.Sort(byIndex(fields))

for i := range fields {
f := &fields[i]
f.encoder = typeEncoder(typeByIndex(t, f.index))
}
return fields
}

Expand Down

0 comments on commit fbb0d1a

Please sign in to comment.