From 2449b35ae8e9c537b51dc23b68b95715d86395cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 2 Apr 2024 15:27:22 +0900 Subject: [PATCH] use sync.OnceFunc instead of sync.Once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sync.OnceFunc was added in Go 1.21 and simplifies the code slightly. Worth noting that, compared to Once, OnceFunc does a bit more work as it replays any panic that happens on the first call: goos: linux goarch: amd64 pkg: github.com/go-json-experiment/json cpu: AMD Ryzen 7 PRO 5850U with Radeon Graphics │ old │ new │ │ sec/op │ sec/op vs base │ Testdata/CanadaGeometry/Marshal/Concrete-8 1.311m ± 0% 1.339m ± 0% +2.17% (p=0.002 n=6) Testdata/CanadaGeometry/Marshal/Interface-8 1.347m ± 1% 1.434m ± 0% +6.46% (p=0.002 n=6) Testdata/CanadaGeometry/Unmarshal/Concrete-8 1.832m ± 0% 1.867m ± 0% +1.92% (p=0.002 n=6) Testdata/CanadaGeometry/Unmarshal/Interface-8 2.962m ± 0% 2.827m ± 1% -4.54% (p=0.002 n=6) geomean 1.759m 1.785m +1.43% --- arshal_default.go | 57 +++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/arshal_default.go b/arshal_default.go index b353bc5..5bc64d3 100644 --- a/arshal_default.go +++ b/arshal_default.go @@ -612,14 +612,13 @@ func makeMapArshaler(t reflect.Type) *arshaler { var fncs arshaler var ( - once sync.Once keyFncs *arshaler valFncs *arshaler ) - init := func() { + initOnce := sync.OnceFunc(func() { keyFncs = lookupArshaler(t.Key()) valFncs = lookupArshaler(t.Elem()) - } + }) fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error { // Check for cycles. xe := export.Encoder(enc) @@ -661,7 +660,7 @@ func makeMapArshaler(t reflect.Type) *arshaler { } } - once.Do(init) + initOnce() if err := enc.WriteToken(jsontext.ObjectStart); err != nil { return err } @@ -792,7 +791,7 @@ func makeMapArshaler(t reflect.Type) *arshaler { va.SetZero() return nil case '{': - once.Do(init) + initOnce() if va.IsNil() { va.Set(reflect.MakeMap(t)) } @@ -900,19 +899,18 @@ func makeStructArshaler(t reflect.Type) *arshaler { var fncs arshaler var ( - once sync.Once fields structFields errInit *SemanticError ) - init := func() { + initOnce := sync.OnceFunc(func() { fields, errInit = makeStructFields(t) - } + }) fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error { xe := export.Encoder(enc) if mo.Format != "" && mo.FormatDepth == xe.Tokens.Depth() { return newInvalidFormatError("marshal", t, mo.Format) } - once.Do(init) + initOnce() if errInit != nil { err := *errInit // shallow copy SemanticError err.action = "marshal" @@ -1080,7 +1078,7 @@ func makeStructArshaler(t reflect.Type) *arshaler { va.SetZero() return nil case '{': - once.Do(init) + initOnce() if errInit != nil { err := *errInit // shallow copy SemanticError err.action = "unmarshal" @@ -1214,13 +1212,10 @@ func isLegacyEmpty(v addressableValue) bool { func makeSliceArshaler(t reflect.Type) *arshaler { var fncs arshaler - var ( - once sync.Once - valFncs *arshaler - ) - init := func() { + var valFncs *arshaler + initOnce := sync.OnceFunc(func() { valFncs = lookupArshaler(t.Elem()) - } + }) fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error { // Check for cycles. xe := export.Encoder(enc) @@ -1262,7 +1257,7 @@ func makeSliceArshaler(t reflect.Type) *arshaler { } } - once.Do(init) + initOnce() if err := enc.WriteToken(jsontext.ArrayStart); err != nil { return err } @@ -1303,7 +1298,7 @@ func makeSliceArshaler(t reflect.Type) *arshaler { va.SetZero() return nil case '[': - once.Do(init) + initOnce() unmarshal := valFncs.unmarshal if uo.Unmarshalers != nil { unmarshal, _ = uo.Unmarshalers.(*Unmarshalers).lookup(unmarshal, t.Elem()) @@ -1348,20 +1343,17 @@ func makeSliceArshaler(t reflect.Type) *arshaler { func makeArrayArshaler(t reflect.Type) *arshaler { var fncs arshaler - var ( - once sync.Once - valFncs *arshaler - ) - init := func() { + var valFncs *arshaler + initOnce := sync.OnceFunc(func() { valFncs = lookupArshaler(t.Elem()) - } + }) n := t.Len() fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error { xe := export.Encoder(enc) if mo.Format != "" && mo.FormatDepth == xe.Tokens.Depth() { return newInvalidFormatError("marshal", t, mo.Format) } - once.Do(init) + initOnce() if err := enc.WriteToken(jsontext.ArrayStart); err != nil { return err } @@ -1395,7 +1387,7 @@ func makeArrayArshaler(t reflect.Type) *arshaler { va.SetZero() return nil case '[': - once.Do(init) + initOnce() unmarshal := valFncs.unmarshal if uo.Unmarshalers != nil { unmarshal, _ = uo.Unmarshalers.(*Unmarshalers).lookup(unmarshal, t.Elem()) @@ -1441,13 +1433,10 @@ func makeArrayArshaler(t reflect.Type) *arshaler { func makePointerArshaler(t reflect.Type) *arshaler { var fncs arshaler - var ( - once sync.Once - valFncs *arshaler - ) - init := func() { + var valFncs *arshaler + initOnce := sync.OnceFunc(func() { valFncs = lookupArshaler(t.Elem()) - } + }) fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error { // Check for cycles. xe := export.Encoder(enc) @@ -1462,7 +1451,7 @@ func makePointerArshaler(t reflect.Type) *arshaler { if va.IsNil() { return enc.WriteToken(jsontext.Null) } - once.Do(init) + initOnce() marshal := valFncs.marshal if mo.Marshalers != nil { marshal, _ = mo.Marshalers.(*Marshalers).lookup(marshal, t.Elem()) @@ -1479,7 +1468,7 @@ func makePointerArshaler(t reflect.Type) *arshaler { va.SetZero() return nil } - once.Do(init) + initOnce() unmarshal := valFncs.unmarshal if uo.Unmarshalers != nil { unmarshal, _ = uo.Unmarshalers.(*Unmarshalers).lookup(unmarshal, t.Elem())