https://github.com/golang/go/blob/089e37a931d30d4055c7468facb602c4cfa9b537/src/net/http/transport.go#L2858 Is it possible to reuse this gzip reader from a sync.Pool to reduce the memory allocation cause by flate.NewReader

Comment From: bcmills

gzip.Reader does have a Reset method, but we would also need some way to know when the gzip.Reader is safe to reuse.

Hmm. (*http.gzipReader).Read already has some locking in Read. Perhaps we could do something like:

func (gz *gzipReader) Read(p []byte) (n int, err error) {
    gz.body.mu.Lock()
    if gz.body.closed {
        err = errReadOnClosedResBody
    } else if gz.zr == nil {
        if gz.zerr == nil {
            zr := gzipPool.Get().(*gzip.Reader)
            gz.zerr = zr.Reset(gz.body)
            if gz.zerr == nil {
                gz.zr = zr
            } else {
                gzipPool.Put(zr)
            }
        }
        err = gz.zerr
    }
    gz.body.mu.Unlock()

    if err != nil {
        return 0, err
    }
    return gz.zr.Read(p)
}

func (gz *gzipReader) Close() error {
    err := gz.body.Close()

    gz.body.mu.Lock()
    if gz.zr != nil {
        gz.zr.Reset(emptyReader{})  // drop reference to gz.body
        gzipPool.Put(gz.zr)
        gz.zr = nil
    }
    gz.body.mu.Unlock()

    return err
}

var (
    gzipPool = sync.Pool{
        New: func() any { return new(gzip.Reader) },
    }
    _ flate.Reader = emptyReader{}
)

type emptyReader struct{}

func (emptyReader) Read([]byte) (int, error) { return 0, io.EOF }
func (emptyReader) ReadByte() (byte, error) { return 0, io.EOF }

Of course, such a change would also need a supporting benchmark. @Sunyue, want to send it for Go 1.22?

(CC @neild)

Comment From: neild

Seems like something we could do; I'm happy to review a CL if someone wants to send me one.

Comment From: gopherbot

Change https://go.dev/cl/510115 mentions this issue: net/http: pool transport gzip readers

Comment From: gopherbot

Change https://go.dev/cl/510255 mentions this issue: net/http: pool transport gzip readers

Comment From: AlexanderYastrebov

Created https://github.com/golang/go/pull/61390

Comment From: unclejack

Is there any technical blocker for these changes? The use of a pool for the gzip.Reader will help avoid memory allocations.