mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
encoding/json/jsontext: avoid pinning application data in pools
Previously, we put a jsontext.Encoder (or Decoder) back into a pool with minimal reset logic. This was semantically safe since we always did a full reset after obtaining an Encoder/Decoder back out of the pool. However, this meant that so long as an Encoder/Decoder was alive in the pool, any application data referenced by the coder would be kept alive longer than necessary. Explicitly, clear such fields so that application data can be more aggressively garbage collected. Performance: name old time/op new time/op delta Unmarshal/Bool-32 52.0ns ± 3% 50.3ns ± 3% -3.30% (p=0.001 n=10+10) Marshal/Bool-32 55.4ns ± 3% 54.4ns ± 2% -1.75% (p=0.006 n=10+9) This only impacts the performance of discrete Marshal/Unmarshal calls. For the simplest possible call (i.e., to marsha/unmarshal a bool), there is a 1-2ns slow down. This is an appropriate slowdown for an improvement in memory utilization. Change-Id: I5e7d7827473773e53a9dcb3d7fe9052a75481e9f Reviewed-on: https://go-review.googlesource.com/c/go/+/713640 Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> TryBot-Bypass: Damien Neil <dneil@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Daniel Martí <mvdan@mvdan.cc> Auto-Submit: Damien Neil <dneil@google.com>
This commit is contained in:
parent
a6a59f0762
commit
f5f69a3de9
2 changed files with 13 additions and 0 deletions
|
|
@ -54,6 +54,10 @@ func getBufferedEncoder(opts ...Options) *Encoder {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
func putBufferedEncoder(e *Encoder) {
|
func putBufferedEncoder(e *Encoder) {
|
||||||
|
if cap(e.s.availBuffer) > 64<<10 {
|
||||||
|
e.s.availBuffer = nil // avoid pinning arbitrarily large amounts of memory
|
||||||
|
}
|
||||||
|
|
||||||
// Recycle large buffers only if sufficiently utilized.
|
// Recycle large buffers only if sufficiently utilized.
|
||||||
// If a buffer is under-utilized enough times sequentially,
|
// If a buffer is under-utilized enough times sequentially,
|
||||||
// then it is discarded, ensuring that a single large buffer
|
// then it is discarded, ensuring that a single large buffer
|
||||||
|
|
@ -95,9 +99,14 @@ func getStreamingEncoder(w io.Writer, opts ...Options) *Encoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func putStreamingEncoder(e *Encoder) {
|
func putStreamingEncoder(e *Encoder) {
|
||||||
|
if cap(e.s.availBuffer) > 64<<10 {
|
||||||
|
e.s.availBuffer = nil // avoid pinning arbitrarily large amounts of memory
|
||||||
|
}
|
||||||
if _, ok := e.s.wr.(*bytes.Buffer); ok {
|
if _, ok := e.s.wr.(*bytes.Buffer); ok {
|
||||||
|
e.s.wr, e.s.Buf = nil, nil // avoid pinning the provided bytes.Buffer
|
||||||
bytesBufferEncoderPool.Put(e)
|
bytesBufferEncoderPool.Put(e)
|
||||||
} else {
|
} else {
|
||||||
|
e.s.wr = nil // avoid pinning the provided io.Writer
|
||||||
if cap(e.s.Buf) > 64<<10 {
|
if cap(e.s.Buf) > 64<<10 {
|
||||||
e.s.Buf = nil // avoid pinning arbitrarily large amounts of memory
|
e.s.Buf = nil // avoid pinning arbitrarily large amounts of memory
|
||||||
}
|
}
|
||||||
|
|
@ -126,6 +135,7 @@ func getBufferedDecoder(b []byte, opts ...Options) *Decoder {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
func putBufferedDecoder(d *Decoder) {
|
func putBufferedDecoder(d *Decoder) {
|
||||||
|
d.s.buf = nil // avoid pinning the provided buffer
|
||||||
bufferedDecoderPool.Put(d)
|
bufferedDecoderPool.Put(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,8 +152,10 @@ func getStreamingDecoder(r io.Reader, opts ...Options) *Decoder {
|
||||||
}
|
}
|
||||||
func putStreamingDecoder(d *Decoder) {
|
func putStreamingDecoder(d *Decoder) {
|
||||||
if _, ok := d.s.rd.(*bytes.Buffer); ok {
|
if _, ok := d.s.rd.(*bytes.Buffer); ok {
|
||||||
|
d.s.rd, d.s.buf = nil, nil // avoid pinning the provided bytes.Buffer
|
||||||
bytesBufferDecoderPool.Put(d)
|
bytesBufferDecoderPool.Put(d)
|
||||||
} else {
|
} else {
|
||||||
|
d.s.rd = nil // avoid pinning the provided io.Reader
|
||||||
if cap(d.s.buf) > 64<<10 {
|
if cap(d.s.buf) > 64<<10 {
|
||||||
d.s.buf = nil // avoid pinning arbitrarily large amounts of memory
|
d.s.buf = nil // avoid pinning arbitrarily large amounts of memory
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -571,5 +571,6 @@ func putStrings(s *stringSlice) {
|
||||||
if cap(*s) > 1<<10 {
|
if cap(*s) > 1<<10 {
|
||||||
*s = nil // avoid pinning arbitrarily large amounts of memory
|
*s = nil // avoid pinning arbitrarily large amounts of memory
|
||||||
}
|
}
|
||||||
|
clear(*s) // avoid pinning a reference to each string
|
||||||
stringsPools.Put(s)
|
stringsPools.Put(s)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue