Commit acbed037 authored by Phil Pearl's avatar Phil Pearl Committed by Daniel Martí

encoding/json: remove allocation when using a Marshaler with value receiver

If we marshal a non-pointer struct field whose type implements Marshaler with
a non-pointer receiver, then we avoid an allocation if we take the address of
the field before casting it to an interface.

name               old time/op    new time/op    delta
EncodeMarshaler-8     104ns ± 1%      92ns ± 2%  -11.72%  (p=0.001 n=7+7)

name               old alloc/op   new alloc/op   delta
EncodeMarshaler-8     36.0B ± 0%      4.0B ± 0%  -88.89%  (p=0.000 n=8+8)

name               old allocs/op  new allocs/op  delta
EncodeMarshaler-8      2.00 ± 0%      1.00 ± 0%  -50.00%  (p=0.000 n=8+8)

Test coverage already looks good enough for this change. TestRefValMarshal
already covers all possible combinations of value & pointer receivers on
value and pointer struct fields.

Change-Id: I6fc7f72396396d98f9a90c3c86e813690f41c099
Reviewed-on: https://go-review.googlesource.com/c/go/+/203608Reviewed-by: default avatarDaniel Martí <mvdan@mvdan.cc>
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 33e3983d
...@@ -399,19 +399,22 @@ var ( ...@@ -399,19 +399,22 @@ var (
// newTypeEncoder constructs an encoderFunc for a type. // newTypeEncoder constructs an encoderFunc for a type.
// The returned encoder only checks CanAddr when allowAddr is true. // The returned encoder only checks CanAddr when allowAddr is true.
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
if t.Implements(marshalerType) { // If we have a non-pointer value whose type implements
return marshalerEncoder // Marshaler with a value receiver, then we're better off taking
} // the address of the value - otherwise we end up with an
// allocation as we cast the value to an interface.
if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(marshalerType) { if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(marshalerType) {
return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false)) return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
} }
if t.Implements(marshalerType) {
if t.Implements(textMarshalerType) { return marshalerEncoder
return textMarshalerEncoder
} }
if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(textMarshalerType) { if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(textMarshalerType) {
return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false)) return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))
} }
if t.Implements(textMarshalerType) {
return textMarshalerEncoder
}
switch t.Kind() { switch t.Kind() {
case reflect.Bool: case reflect.Bool:
......
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