]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: remove allocation when using a Marshaler with value receiver
authorPhil Pearl <philip.j.r.pearl@gmail.com>
Sun, 27 Oct 2019 16:05:54 +0000 (16:05 +0000)
committerDaniel Martí <mvdan@mvdan.cc>
Mon, 28 Oct 2019 11:22:20 +0000 (11:22 +0000)
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>

src/encoding/json/encode.go

index a7473a7eba5d9f208ac8314dd8d10e1a8c20f9f1..b81e5058660f73dbd4bfaf10c853e973562fcf86 100644 (file)
@@ -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: