diff options
| author | Phil Pearl <philip.j.r.pearl@gmail.com> | 2019-10-27 16:05:54 +0000 |
|---|---|---|
| committer | Daniel Martí <mvdan@mvdan.cc> | 2019-10-28 11:22:20 +0000 |
| commit | acbed0372ea000db8b1ea69eca9d7acecdf89469 (patch) | |
| tree | bb95e2140acc362224ee8071fc9642ad5d511f02 /src/encoding/json | |
| parent | 33e3983db805ccf27f34a143d91e4346233a0ee2 (diff) | |
| download | go-acbed0372ea000db8b1ea69eca9d7acecdf89469.tar.xz | |
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/+/203608
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/encoding/json')
| -rw-r--r-- | src/encoding/json/encode.go | 15 |
1 files changed, 9 insertions, 6 deletions
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index a7473a7eba..b81e505866 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -399,19 +399,22 @@ var ( // newTypeEncoder constructs an encoderFunc for a type. // The returned encoder only checks CanAddr when allowAddr is true. func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { - if t.Implements(marshalerType) { - return marshalerEncoder - } + // If we have a non-pointer value whose type implements + // 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) { return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false)) } - - if t.Implements(textMarshalerType) { - return textMarshalerEncoder + if t.Implements(marshalerType) { + return marshalerEncoder } if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(textMarshalerType) { return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false)) } + if t.Implements(textMarshalerType) { + return textMarshalerEncoder + } switch t.Kind() { case reflect.Bool: |
