From 2828fa21c0ffd2a32a108b37c0417b01abc42929 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Thu, 26 Jan 2023 21:02:56 -0700 Subject: Avoid doing type switching in inner functions The CPU profiler tells me that the encoder is spending a lot of time on type switches. --- decode.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'decode.go') diff --git a/decode.go b/decode.go index 7ae723c..91be865 100644 --- a/decode.go +++ b/decode.go @@ -565,7 +565,7 @@ func (dec *Decoder) decode(val reflect.Value, nullOK bool) { if dec.disallowUnknownFields { dec.panicType("", typ, fmt.Errorf("json: unknown field %q", name)) } - dec.scan(io.Discard) + dec.scan(internal.Discard) return } field := index.byPos[idx] @@ -749,7 +749,7 @@ func (dec *Decoder) decode(val reflect.Value, nullOK bool) { dec.decode(mValPtr.Elem(), false) val.Index(i).Set(mValPtr.Elem()) } else { - dec.scan(io.Discard) + dec.scan(internal.Discard) } i++ }) @@ -773,18 +773,18 @@ func (dec *Decoder) decode(val reflect.Value, nullOK bool) { } } -func (dec *Decoder) scan(out io.Writer) { +func (dec *Decoder) scan(out internal.RuneWriter) { limiter := dec.limitingScanner() for { c, _, err := limiter.ReadRune() if err == io.EOF { return } - _, _ = writeRune(out, c) + _, _ = out.WriteRune(c) } } -func (dec *Decoder) scanNumber(gTyp reflect.Type, out io.Writer) { +func (dec *Decoder) scanNumber(gTyp reflect.Type, out internal.RuneWriter) { if t := dec.peekRuneType(); !t.IsNumber() { dec.panicType(t.JSONType(), gTyp, nil) } @@ -991,34 +991,34 @@ func (dec *Decoder) decodeArray(gTyp reflect.Type, decodeMember func()) { } } -func (dec *Decoder) decodeString(gTyp reflect.Type, out io.Writer) { +func (dec *Decoder) decodeString(gTyp reflect.Type, out internal.RuneWriter) { dec.expectRuneType('"', internal.RuneTypeStringBeg, gTyp) var uhex [4]byte for { c, t := dec.readRune() switch t { case internal.RuneTypeStringChar: - _, _ = writeRune(out, c) + _, _ = out.WriteRune(c) case internal.RuneTypeStringEsc, internal.RuneTypeStringEscU: // do nothing case internal.RuneTypeStringEsc1: switch c { case '"': - _, _ = writeRune(out, '"') + _, _ = out.WriteRune('"') case '\\': - _, _ = writeRune(out, '\\') + _, _ = out.WriteRune('\\') case '/': - _, _ = writeRune(out, '/') + _, _ = out.WriteRune('/') case 'b': - _, _ = writeRune(out, '\b') + _, _ = out.WriteRune('\b') case 'f': - _, _ = writeRune(out, '\f') + _, _ = out.WriteRune('\f') case 'n': - _, _ = writeRune(out, '\n') + _, _ = out.WriteRune('\n') case 'r': - _, _ = writeRune(out, '\r') + _, _ = out.WriteRune('\r') case 't': - _, _ = writeRune(out, '\t') + _, _ = out.WriteRune('\t') default: panic("should not happen") } @@ -1038,12 +1038,12 @@ func (dec *Decoder) decodeString(gTyp reflect.Type, out io.Writer) { handleUnicode: if utf16.IsSurrogate(c) { if dec.peekRuneType() != internal.RuneTypeStringEsc { - _, _ = writeRune(out, utf8.RuneError) + _, _ = out.WriteRune(utf8.RuneError) break } dec.expectRune('\\', internal.RuneTypeStringEsc) if dec.peekRuneType() != internal.RuneTypeStringEscU { - _, _ = writeRune(out, utf8.RuneError) + _, _ = out.WriteRune(utf8.RuneError) break } dec.expectRune('u', internal.RuneTypeStringEscU) @@ -1063,13 +1063,13 @@ func (dec *Decoder) decodeString(gTyp reflect.Type, out io.Writer) { rune(uhex[3])<<0 d := utf16.DecodeRune(c, c2) if d == utf8.RuneError { - _, _ = writeRune(out, utf8.RuneError) + _, _ = out.WriteRune(utf8.RuneError) c = c2 goto handleUnicode } - _, _ = writeRune(out, d) + _, _ = out.WriteRune(d) } else { - _, _ = writeRune(out, c) + _, _ = out.WriteRune(c) } case internal.RuneTypeStringEnd: return -- cgit v1.2.3-2-g168b From bc1bacc410ddfa444c5bf0e56f33a7da440658ae Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 30 Jan 2023 11:42:51 -0700 Subject: decode: Inline noWsRuneTypeScanner into runeTypeScannerImpl --- decode.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'decode.go') diff --git a/decode.go b/decode.go index 91be865..8fab267 100644 --- a/decode.go +++ b/decode.go @@ -104,12 +104,10 @@ const maxNestingDepth = 10000 // an io.Reader. func NewDecoder(r io.RuneScanner) *Decoder { return &Decoder{ - io: &noWSRuneTypeScanner{ - inner: &runeTypeScannerImpl{ - inner: r, - parser: internal.Parser{ - MaxDepth: maxNestingDepth, - }, + io: &runeTypeScannerImpl{ + inner: r, + parser: internal.Parser{ + MaxDepth: maxNestingDepth, }, }, } -- cgit v1.2.3-2-g168b From b3f4186f2b8e992f56f898784b1cd28bfd7550ca Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 29 Jan 2023 20:59:37 -0700 Subject: Invent "barriers" instead of nesting parsers --- decode.go | 56 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 23 deletions(-) (limited to 'decode.go') diff --git a/decode.go b/decode.go index 8fab267..60b530f 100644 --- a/decode.go +++ b/decode.go @@ -104,7 +104,7 @@ const maxNestingDepth = 10000 // an io.Reader. func NewDecoder(r io.RuneScanner) *Decoder { return &Decoder{ - io: &runeTypeScannerImpl{ + io: runeTypeScanner{ inner: r, parser: internal.Parser{ MaxDepth: maxNestingDepth, @@ -245,6 +245,7 @@ func (dec *Decoder) Decode(ptr any) (err error) { } dec.io.Reset() + dec.io.PushReadBarrier() defer func() { if r := recover(); r != nil { if de, ok := r.(decodeError); ok { @@ -257,6 +258,7 @@ func (dec *Decoder) Decode(ptr any) (err error) { } }() dec.decode(ptrVal.Elem(), false) + dec.io.PopReadBarrier() return nil } @@ -319,12 +321,21 @@ func (dec *Decoder) expectRuneType(ec rune, et internal.RuneType, gt reflect.Typ } } -type decRuneTypeScanner struct { +type decRuneScanner struct { dec *Decoder + eof bool } -func (sc *decRuneTypeScanner) ReadRuneType() (rune, int, internal.RuneType, error) { +func (sc *decRuneScanner) ReadRune() (rune, int, error) { + if sc.eof { + return 0, 0, io.EOF + } c, s, t, e := sc.dec.io.ReadRuneType() + if t == internal.RuneTypeEOF { + sc.eof = true + sc.dec.io.PopReadBarrier() + return 0, 0, io.EOF + } if e != nil { panic(decodeError{ Field: sc.dec.structStackStr(), @@ -333,28 +344,17 @@ func (sc *decRuneTypeScanner) ReadRuneType() (rune, int, internal.RuneType, erro Err: e, }) } - return c, s, t, nil + return c, s, nil } -func (sc *decRuneTypeScanner) ReadRune() (rune, int, error) { - r, s, t, _ := sc.ReadRuneType() - switch t { - case internal.RuneTypeEOF: - return 0, 0, io.EOF - default: - return r, s, nil - } +func (sc *decRuneScanner) UnreadRune() error { + return sc.dec.io.UnreadRune() } -func (sc *decRuneTypeScanner) UnreadRune() error { return sc.dec.io.UnreadRune() } -func (sc *decRuneTypeScanner) InputOffset() int64 { return sc.dec.InputOffset() } -func (sc *decRuneTypeScanner) Reset() { sc.dec.io.Reset() } - -func (dec *Decoder) limitingScanner() runeTypeScanner { - return &elemRuneTypeScanner{ - inner: &decRuneTypeScanner{ - dec: dec, - }, +func (dec *Decoder) limitingScanner() io.RuneScanner { + dec.io.PushReadBarrier() + return &decRuneScanner{ + dec: dec, } } @@ -867,7 +867,12 @@ func DecodeObject(r io.RuneScanner, decodeKey, decodeVal func(io.RuneScanner) er } } }() - dec := NewDecoder(r) + var dec *Decoder + if dr, ok := r.(*decRuneScanner); ok { + dec = dr.dec + } else { + dec = NewDecoder(r) + } dec.posStackPush() defer dec.posStackPop() dec.decodeObject(nil, @@ -947,7 +952,12 @@ func DecodeArray(r io.RuneScanner, decodeMember func(r io.RuneScanner) error) (e } } }() - dec := NewDecoder(r) + var dec *Decoder + if dr, ok := r.(*decRuneScanner); ok { + dec = dr.dec + } else { + dec = NewDecoder(r) + } dec.posStackPush() defer dec.posStackPop() dec.decodeArray(nil, func() { -- cgit v1.2.3-2-g168b