Commit ab2f83c3 authored by Daniel Martí's avatar Daniel Martí

encoding/json: remove a bounds check in readValue

readValue is a hot function, clocking in at ~13% flat CPU use in
CodeDecoder. In particular, looping over the bytes is slow. That's
partially because the code contains a bounds check at the start of the
loop.

The source of the problem is that scanp is a signed integer, and comes
from a field, so the compiler doesn't know that it's non-negative. Help
it with a simple and comparatively cheap hint.

While at it, use scanp as the index variable directly, removing the need
for a duplicate index variable which is later added back into scanp.

name           old time/op    new time/op    delta
CodeDecoder-8    11.3ms ± 1%    11.2ms ± 1%  -0.98%  (p=0.000 n=9+9)

name           old speed      new speed      delta
CodeDecoder-8   172MB/s ± 1%   174MB/s ± 1%  +0.99%  (p=0.000 n=9+9)

Updates #28923.

Change-Id: I138f83babdf316fc97697cc18f595c3403c1ddb7
Reviewed-on: https://go-review.googlesource.com/c/go/+/170939
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent ee64b355
...@@ -92,20 +92,23 @@ func (dec *Decoder) readValue() (int, error) { ...@@ -92,20 +92,23 @@ func (dec *Decoder) readValue() (int, error) {
scanp := dec.scanp scanp := dec.scanp
var err error var err error
Input: Input:
for { // help the compiler see that scanp is never negative, so it can remove
// some bounds checks below.
for scanp >= 0 {
// Look in the buffer for a new value. // Look in the buffer for a new value.
for i, c := range dec.buf[scanp:] { for ; scanp < len(dec.buf); scanp++ {
c := dec.buf[scanp]
dec.scan.bytes++ dec.scan.bytes++
switch dec.scan.step(&dec.scan, c) { switch dec.scan.step(&dec.scan, c) {
case scanEnd: case scanEnd:
scanp += i
break Input break Input
case scanEndObject, scanEndArray: case scanEndObject, scanEndArray:
// scanEnd is delayed one byte. // scanEnd is delayed one byte.
// We might block trying to get that byte from src, // We might block trying to get that byte from src,
// so instead invent a space byte. // so instead invent a space byte.
if stateEndValue(&dec.scan, ' ') == scanEnd { if stateEndValue(&dec.scan, ' ') == scanEnd {
scanp += i + 1 scanp++
break Input break Input
} }
case scanError: case scanError:
...@@ -113,7 +116,6 @@ Input: ...@@ -113,7 +116,6 @@ Input:
return 0, dec.scan.err return 0, dec.scan.err
} }
} }
scanp = len(dec.buf)
// Did the last read have an error? // Did the last read have an error?
// Delayed until now to allow buffer scan. // Delayed until now to allow buffer scan.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment