Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 041cc00

Browse files
net/http: pool transport gzip readers
name old time/op new time/op delta GzipReaderInit-8 8.97μs ± 6% 2.61μs ± 7% -70.87% (p=0.000 n=10+9) name old alloc/op new alloc/op delta GzipReaderInit-8 45.5kB ± 0% 4.4kB ± 0% -90.36% (p=0.000 n=10+10) name old allocs/op new allocs/op delta GzipReaderInit-8 10.0 ± 0% 6.0 ± 0% -40.00% (p=0.000 n=10+10) Allocation saving comes from absent compress/flate.(*dictDecoder).init Fixes #61353
1 parent ce0f28e commit 041cc00

File tree

1 file changed

+61
-15
lines changed

1 file changed

+61
-15
lines changed

‎src/net/http/transport.go

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package http
1111

1212
import (
1313
"bufio"
14+
"compress/flate"
1415
"compress/gzip"
1516
"container/list"
1617
"context"
@@ -2844,37 +2845,82 @@ func (es *bodyEOFSignal) condfn(err error) error {
28442845
}
28452846

28462847
// gzipReader wraps a response body so it can lazily
2847-
// call gzip.NewReader on the first call to Read
2848+
// get gzip.Reader from the pool on the first call to Read.
2849+
// After Close is called it puts gzip.Reader to the pool immediately
2850+
// if there is no Read in progress or later when Read completes.
28482851
type gzipReader struct {
28492852
_ incomparable
28502853
body *bodyEOFSignal // underlying HTTP/1 response body framing
2851-
zr *gzip.Reader // lazily-initialized gzip reader
2852-
zerr error // any error from gzip.NewReader; sticky
2854+
2855+
mu sync.Mutex
2856+
zr *gzip.Reader // lazily-initialized gzip reader
2857+
zerr error // any error from gzip.Reader.Reset; sticky
2858+
reading bool // true if Read() is in progress
2859+
closed bool // true if Close() was called
2860+
}
2861+
2862+
type eofReader struct{}
2863+
2864+
func (eofReader) Read([]byte) (int, error) { return 0, io.EOF }
2865+
func (eofReader) ReadByte() (byte, error) { return 0, io.EOF }
2866+
2867+
var (
2868+
gzipPool = sync.Pool{New: func() any { return new(gzip.Reader) }}
2869+
2870+
// parkedReader used to reset gzip.Reader before returning into the gzipPool as
2871+
// gzip.Reader.Reset(flate.Reader) avoids calling bufio.NewReader
2872+
parkedReader flate.Reader = eofReader{}
2873+
)
2874+
2875+
func gzipPoolPut(zr *gzip.Reader) {
2876+
_ = zr.Reset(parkedReader)
2877+
gzipPool.Put(zr)
28532878
}
28542879

28552880
func (gz *gzipReader) Read(p []byte) (n int, err error) {
2856-
if gz.zr == nil {
2881+
gz.mu.Lock()
2882+
if gz.closed {
2883+
err = errReadOnClosedResBody
2884+
} else if gz.zr == nil {
28572885
if gz.zerr == nil {
2858-
gz.zr, gz.zerr = gzip.NewReader(gz.body)
2859-
}
2860-
if gz.zerr != nil {
2861-
return 0, gz.zerr
2886+
zr := gzipPool.Get().(*gzip.Reader)
2887+
if gz.zerr = zr.Reset(gz.body); gz.zerr == nil {
2888+
gz.zr = zr
2889+
} else {
2890+
gzipPoolPut(zr)
2891+
}
28622892
}
2893+
err = gz.zerr
28632894
}
2864-
2865-
gz.body.mu.Lock()
2866-
if gz.body.closed {
2867-
err = errReadOnClosedResBody
2868-
}
2869-
gz.body.mu.Unlock()
2895+
gz.reading = err == nil
2896+
gz.mu.Unlock()
28702897

28712898
if err != nil {
28722899
return 0, err
28732900
}
2874-
return gz.zr.Read(p)
2901+
2902+
n, err = gz.zr.Read(p)
2903+
2904+
gz.mu.Lock()
2905+
if gz.closed {
2906+
gzipPoolPut(gz.zr)
2907+
gz.zr = nil
2908+
}
2909+
gz.reading = false
2910+
gz.mu.Unlock()
2911+
2912+
return
28752913
}
28762914

28772915
func (gz *gzipReader) Close() error {
2916+
gz.mu.Lock()
2917+
if !gz.closed && !gz.reading && gz.zr != nil {
2918+
gzipPoolPut(gz.zr)
2919+
gz.zr = nil
2920+
}
2921+
gz.closed = true
2922+
gz.mu.Unlock()
2923+
28782924
return gz.body.Close()
28792925
}
28802926

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /