]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: avoid allocation when decoding number types
authorkorzhao <korzhao95@gmail.com>
Fri, 11 Aug 2023 09:20:36 +0000 (17:20 +0800)
committerDaniel Martí <mvdan@mvdan.cc>
Fri, 25 Aug 2023 08:50:27 +0000 (08:50 +0000)
In CL 345488, we optimized strconv.ParseXXX for []byte arguments.
That allows immediate casting of a []byte to a string that does not escape to be copied on the stack.

Performance:

goos: darwin
goarch: arm64
pkg: encoding/json
old sec/op new sec/op delta
CodeUnmarshal-10        3.019m ± 6%   2.865m ± 10%   -5.10% (p=0.043 n=10)
CodeUnmarshalReuse-10   2.528m ± 4%   2.274m ± 13%  -10.03% (p=0.009 n=10)
geomean                 2.762m        2.553m         -7.60%

old B/s new B/s delta
CodeUnmarshal-10        613.1Mi ± 5%   646.0Mi ±  9%   +5.37% (p=0.043 n=10)
CodeUnmarshalReuse-10   732.1Mi ± 4%   813.7Mi ± 12%  +11.15% (p=0.009 n=10)
geomean                 669.9Mi        725.0Mi         +8.22%

old B/op new B/op delta
CodeUnmarshal-10         2.782Mi ± 0%   1.918Mi ± 0%  -31.04% (p=0.000 n=10)
CodeUnmarshalReuse-10   1600.8Ki ± 0%   713.3Ki ± 0%  -55.44% (p=0.000 n=10)
geomean                  2.085Mi        1.156Mi       -44.57%

old allocs/op  new allocs/op  delta
CodeUnmarshal-10        91.31k ± 0%   39.99k ± 0%  -56.20% (p=0.000 n=10)
CodeUnmarshalReuse-10   76.58k ± 0%   25.23k ± 0%  -67.06% (p=0.000 n=10)
geomean                 83.62k        31.76k       -62.02%

Change-Id: I208c57089040daee0f9d979d1df725e3acf34f81
Reviewed-on: https://go-review.googlesource.com/c/go/+/518277
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Joseph Tsai <joetsai@digital-static.net>
Run-TryBot: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
src/encoding/json/decode.go

index 858a2ed41a1507877336864dee7121bdb27d9204..72188a66f60223e26fa136e3dcbf004c10ad6f46 100644 (file)
@@ -962,13 +962,12 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
                        }
                        panic(phasePanicMsg)
                }
-               s := string(item)
                switch v.Kind() {
                default:
                        if v.Kind() == reflect.String && v.Type() == numberType {
                                // s must be a valid number, because it's
                                // already been tokenized.
-                               v.SetString(s)
+                               v.SetString(string(item))
                                break
                        }
                        if fromQuoted {
@@ -976,7 +975,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
                        }
                        d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())})
                case reflect.Interface:
-                       n, err := d.convertNumber(s)
+                       n, err := d.convertNumber(string(item))
                        if err != nil {
                                d.saveError(err)
                                break
@@ -988,25 +987,25 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
                        v.Set(reflect.ValueOf(n))
 
                case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-                       n, err := strconv.ParseInt(s, 10, 64)
+                       n, err := strconv.ParseInt(string(item), 10, 64)
                        if err != nil || v.OverflowInt(n) {
-                               d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())})
+                               d.saveError(&UnmarshalTypeError{Value: "number " + string(item), Type: v.Type(), Offset: int64(d.readIndex())})
                                break
                        }
                        v.SetInt(n)
 
                case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
-                       n, err := strconv.ParseUint(s, 10, 64)
+                       n, err := strconv.ParseUint(string(item), 10, 64)
                        if err != nil || v.OverflowUint(n) {
-                               d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())})
+                               d.saveError(&UnmarshalTypeError{Value: "number " + string(item), Type: v.Type(), Offset: int64(d.readIndex())})
                                break
                        }
                        v.SetUint(n)
 
                case reflect.Float32, reflect.Float64:
-                       n, err := strconv.ParseFloat(s, v.Type().Bits())
+                       n, err := strconv.ParseFloat(string(item), v.Type().Bits())
                        if err != nil || v.OverflowFloat(n) {
-                               d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())})
+                               d.saveError(&UnmarshalTypeError{Value: "number " + string(item), Type: v.Type(), Offset: int64(d.readIndex())})
                                break
                        }
                        v.SetFloat(n)