From 88b1d6115a9d984780391f11523364dbd509ae14 Mon Sep 17 00:00:00 2001 From: Shengyu Zhang Date: Sat, 30 Mar 2024 03:56:30 +0000 Subject: [PATCH] encoding/json: prevent duplicate slicebytetostring MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit When storing literal to JSON number v, if s is valid number, the slicebytetostring operation will be performed twice. In fact, the operation is unavoidable on any code path, so just perform it at the very beginning. This is not a big optimization, but better than nothing: $ ../bin/go test ./encoding/json/ -bench UnmarshalNumber -run NOTEST -benchtime 10000000x -count 16 > old.txt $ ../bin/go test ./encoding/json/ -bench UnmarshalNumber -run NOTEST -benchtime 10000000x -count 16 > new.txt $ benchstat old.txt new.txt │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ UnmarshalNumber-8 234.5n ± 3% 228.2n ± 4% -2.67% (p=0.033 n=16) │ old.txt │ new.txt │ │ B/op │ B/op vs base │ UnmarshalNumber-8 168.0 ± 0% 168.0 ± 0% ~ (p=1.000 n=16) ¹ ¹ all samples are equal │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ UnmarshalNumber-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=16) ¹ ¹ all samples are equal Change-Id: I1dfdb1ed0883e385f753b2046b7f047c792aa4e3 GitHub-Last-Rev: d236dd7265f110dbb6e0b9b0a824aab9ba7c36be GitHub-Pull-Request: golang/go#61242 Reviewed-on: https://go-review.googlesource.com/c/go/+/508556 LUCI-TryBot-Result: Go LUCI Reviewed-by: Joseph Tsai Reviewed-by: Dmitri Shuralyov Reviewed-by: qiulaidongfeng <2645477756@qq.com> Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor --- src/encoding/json/bench_test.go | 11 +++++++++++ src/encoding/json/decode.go | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/encoding/json/bench_test.go b/src/encoding/json/bench_test.go index f7bcf8073c..032114cac1 100644 --- a/src/encoding/json/bench_test.go +++ b/src/encoding/json/bench_test.go @@ -571,3 +571,14 @@ func BenchmarkNumberIsValidRegexp(b *testing.B) { jsonNumberRegexp.MatchString(s) } } + +func BenchmarkUnmarshalNumber(b *testing.B) { + b.ReportAllocs() + data := []byte(`"-61657.61667E+61673"`) + var number Number + for i := 0; i < b.N; i++ { + if err := Unmarshal(data, &number); err != nil { + b.Fatal("Unmarshal:", err) + } + } +} diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index e1bc9753b1..ce566f7955 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -943,10 +943,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool } v.SetBytes(b[:n]) case reflect.String: - if v.Type() == numberType && !isValidNumber(string(s)) { + t := string(s) + if v.Type() == numberType && !isValidNumber(t) { return fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item) } - v.SetString(string(s)) + v.SetString(t) case reflect.Interface: if v.NumMethod() == 0 { v.Set(reflect.ValueOf(string(s))) -- 2.48.1