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 a632c84

Browse files
net/http: pool transport gzip readers
│ old │ new │ │ sec/op │ sec/op vs base │ ClientGzip-8 649.4μ ± 4% 644.4μ ± 12% ~ (p=0.739 n=10) │ old │ new │ │ B/op │ B/op vs base │ ClientGzip-8 49.486Ki ± 0% 9.270Ki ± 2% -81.27% (p=0.000 n=10) │ old │ new │ │ allocs/op │ allocs/op vs base │ ClientGzip-8 51.00 ± 0% 46.00 ± 0% -9.80% (p=0.000 n=10) Allocation saving comes from absent compress/flate.(*dictDecoder).init Updates #61353
1 parent 087d3ca commit a632c84

File tree

1 file changed

+72
-13
lines changed

1 file changed

+72
-13
lines changed

‎src/net/http/transport.go

Lines changed: 72 additions & 13 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"
@@ -2795,6 +2796,7 @@ type bodyEOFSignal struct {
27952796
}
27962797

27972798
var errReadOnClosedResBody = errors.New("http: read on closed response body")
2799+
var errConcurrentReadOnResBody = errors.New("http: concurrent read on response body")
27982800

27992801
func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
28002802
es.mu.Lock()
@@ -2844,37 +2846,94 @@ func (es *bodyEOFSignal) condfn(err error) error {
28442846
}
28452847

28462848
// gzipReader wraps a response body so it can lazily
2847-
// call gzip.NewReader on the first call to Read
2849+
// get gzip.Reader from the pool on the first call to Read.
2850+
// After Close is called it puts gzip.Reader to the pool immediately
2851+
// if there is no Read in progress or later when Read completes.
28482852
type gzipReader struct {
28492853
_ incomparable
28502854
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
2855+
mu sync.Mutex
2856+
zr *gzip.Reader
2857+
zerr error
28532858
}
28542859

2855-
func (gz *gzipReader) Read(p []byte) (n int, err error) {
2860+
type eofReader struct{}
2861+
2862+
func (eofReader) Read([]byte) (int, error) { return 0, io.EOF }
2863+
func (eofReader) ReadByte() (byte, error) { return 0, io.EOF }
2864+
2865+
var (
2866+
gzipPool = sync.Pool{New: func() any { return new(gzip.Reader) }}
2867+
2868+
// parkedReader used to reset gzip.Reader before returning into the gzipPool as
2869+
// gzip.Reader.Reset(flate.Reader) avoids calling bufio.NewReader
2870+
parkedReader flate.Reader = eofReader{}
2871+
)
2872+
2873+
func gzipPoolGet(r io.Reader) (*gzip.Reader, error) {
2874+
zr := gzipPool.Get().(*gzip.Reader)
2875+
if err := zr.Reset(r); err != nil {
2876+
gzipPoolPut(zr)
2877+
return nil, err
2878+
}
2879+
return zr, nil
2880+
}
2881+
2882+
func gzipPoolPut(zr *gzip.Reader) {
2883+
_ = zr.Reset(parkedReader)
2884+
gzipPool.Put(zr)
2885+
}
2886+
2887+
func (gz *gzipReader) get() (*gzip.Reader, error) {
2888+
gz.mu.Lock()
2889+
defer gz.mu.Unlock()
2890+
if gz.zerr != nil {
2891+
return nil, gz.zerr
2892+
}
28562893
if gz.zr == nil {
2857-
if gz.zerr == nil {
2858-
gz.zr, gz.zerr = gzip.NewReader(gz.body)
2859-
}
2894+
gz.zr, gz.zerr = gzipPoolGet(gz.body)
28602895
if gz.zerr != nil {
2861-
return 0, gz.zerr
2896+
return nil, gz.zerr
28622897
}
28632898
}
2899+
ret := gz.zr
2900+
gz.zr, gz.zerr = nil, errConcurrentReadOnResBody
2901+
return ret, nil
2902+
}
28642903

2865-
gz.body.mu.Lock()
2866-
if gz.body.closed {
2867-
err = errReadOnClosedResBody
2904+
func (gz *gzipReader) put(zr *gzip.Reader) {
2905+
gz.mu.Lock()
2906+
defer gz.mu.Unlock()
2907+
if gz.zerr == errConcurrentReadOnResBody {
2908+
gz.zr, gz.zerr = zr, nil
2909+
} else { // errReadOnClosedResBody
2910+
gzipPoolPut(zr)
28682911
}
2869-
gz.body.mu.Unlock()
2912+
}
28702913

2914+
func (gz *gzipReader) close() {
2915+
gz.mu.Lock()
2916+
defer gz.mu.Unlock()
2917+
if gz.zerr == nil && gz.zr != nil {
2918+
gzipPoolPut(gz.zr)
2919+
gz.zr = nil
2920+
}
2921+
gz.zerr = errReadOnClosedResBody
2922+
}
2923+
2924+
func (gz *gzipReader) Read(p []byte) (n int, err error) {
2925+
zr, err := gz.get()
28712926
if err != nil {
28722927
return 0, err
28732928
}
2874-
return gz.zr.Read(p)
2929+
defer gz.put(zr)
2930+
2931+
return zr.Read(p)
28752932
}
28762933

28772934
func (gz *gzipReader) Close() error {
2935+
gz.close()
2936+
28782937
return gz.body.Close()
28792938
}
28802939

0 commit comments

Comments
(0)

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