]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: prevent duplicate slicebytetostring
authorShengyu Zhang <i@silverrainz.me>
Sat, 30 Mar 2024 03:56:30 +0000 (03:56 +0000)
committerGopher Robot <gobot@golang.org>
Tue, 2 Apr 2024 15:00:17 +0000 (15:00 +0000)
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 <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>

src/encoding/json/bench_test.go
src/encoding/json/decode.go

index f7bcf8073c0f1cf5113247c93eecc509ec851da4..032114cac14b8ec981a083f3e98541cd5508929d 100644 (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)
+               }
+       }
+}
index e1bc9753b11361808a5c16f50db763202e4352c3..ce566f7955692581cf477f0545c2338eae3f9b2b 100644 (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)))