encoding/json: prevent duplicate slicebytetostring

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: d236dd7265
GitHub-Pull-Request: golang/go#61242
Reviewed-on: https://go-review.googlesource.com/c/go/+/508556
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: qiulaidongfeng <2645477756@qq.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Shengyu Zhang 2024-03-30 03:56:30 +00:00 committed by Gopher Robot
parent 0611816216
commit 88b1d6115a
2 changed files with 14 additions and 2 deletions

View file

@ -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)
}
}
}

View file

@ -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)))