mirror of
https://github.com/golang/go
synced 2024-11-11 23:20:24 -07:00
encoding/json: Remove extra allocation in scanner.
When the scanner receives a non-whitespace character in stateEndTop, it creates an error message and caches it to return on the next transition. nextValue() uses the scanner to sub-scan for a value inside a larger JSON structure. Since stateEndTop is triggered *after* the ending byte, whatever character immediately follows the sub-value gets pulled into the scanner's state machine as well. Even though it is not used and doesn't cause an error, it does cause the state machine to allocate an error that will never be used. The fix is to probe the state machine with whitespace after scanEndObject or scanEndArray to see if the next character would result in a scanEnd state transition. If so, we can return right away without processing the next character and avoid triggering an allocation. benchmark old ns/op new ns/op delta BenchmarkCodeEncoder 17022194 16611336 -2.41% BenchmarkCodeMarshal 18443250 18090144 -1.91% BenchmarkCodeDecoder 61502053 61010936 -0.80% BenchmarkCodeUnmarshal 61410829 60363605 -1.71% BenchmarkCodeUnmarshalReuse 59124836 58361772 -1.29% BenchmarkUnmarshalString 602 603 +0.17% BenchmarkUnmarshalFloat64 535 537 +0.37% BenchmarkUnmarshalInt64 482 482 +0.00% BenchmarkIssue10335 1206 799 -33.75% BenchmarkSkipValue 17605751 18355391 +4.26% BenchmarkEncoderEncode 612 604 -1.31% benchmark old MB/s new MB/s speedup BenchmarkCodeEncoder 114.00 116.82 1.02x BenchmarkCodeMarshal 105.21 107.27 1.02x BenchmarkCodeDecoder 31.55 31.81 1.01x BenchmarkCodeUnmarshal 31.60 32.15 1.02x BenchmarkSkipValue 111.63 107.07 0.96x benchmark old allocs new allocs delta BenchmarkIssue10335 11 4 -63.64% BenchmarkEncoderEncode 2 2 +0.00% benchmark old bytes new bytes delta BenchmarkIssue10335 376 272 -27.66% BenchmarkEncoderEncode 40 40 +0.00% Fixes #10335 Change-Id: I3d4f2b67f7a038adfb33ba48bb6b680f528baf18 Reviewed-on: https://go-review.googlesource.com/9074 Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
4f4da07ea0
commit
a13606e619
@ -187,3 +187,14 @@ func BenchmarkUnmarshalInt64(b *testing.B) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIssue10335(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
var s struct{}
|
||||
j := []byte(`{"a":{ }}`)
|
||||
for n := 0; n < b.N; n++ {
|
||||
if err := Unmarshal(j, &s); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,8 +38,15 @@ func nextValue(data []byte, scan *scanner) (value, rest []byte, err error) {
|
||||
scan.reset()
|
||||
for i, c := range data {
|
||||
v := scan.step(scan, int(c))
|
||||
if v >= scanEnd {
|
||||
if v >= scanEndObject {
|
||||
switch v {
|
||||
// probe the scanner with a space to determine whether we will
|
||||
// get scanEnd on the next character. Otherwise, if the next character
|
||||
// is not a space, scanEndTop allocates a needless error.
|
||||
case scanEndObject, scanEndArray:
|
||||
if scan.step(scan, ' ') == scanEnd {
|
||||
return data[:i+1], data[i+1:], nil
|
||||
}
|
||||
case scanError:
|
||||
return nil, nil, scan.err
|
||||
case scanEnd:
|
||||
|
Loading…
Reference in New Issue
Block a user