aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2022-12-09 09:56:15 -0800
committerGopher Robot <gobot@golang.org>2023-01-31 17:44:33 +0000
commit7d7fd6d3627f7dbba250ecfecea2f5ffe6c5aaa7 (patch)
tree74c7f6f593d90af07cf2c835501b26f364767ef1
parentadc1db23ee17078590d81fb2201a12212d539628 (diff)
downloadgo-7d7fd6d3627f7dbba250ecfecea2f5ffe6c5aaa7.tar.xz
io: allocate copy buffers from a pool
CopyBuffer allocates a 32k buffer when no buffer is available. Allocate these buffers from a sync.Pool. This removes an optimization where the copy buffer size was reduced when the source is a io.LimitedReader (including the case of CopyN) with a limit less than the default buffer size. This change could cause a program which only uses io.Copy with sources with a small limit to allocate unnecessarily large buffers. Programs which care about the transient buffer allocation can avoid this by providing their own buffer. name old time/op new time/op delta CopyNSmall-10 165ns ± 7% 117ns ± 7% -29.19% (p=0.001 n=7+7) CopyNLarge-10 7.33µs ±34% 4.07µs ± 2% -44.52% (p=0.001 n=7+7) name old alloc/op new alloc/op delta CopyNSmall-10 2.20kB ±12% 1.20kB ± 4% -45.24% (p=0.000 n=8+7) CopyNLarge-10 148kB ± 9% 81kB ± 4% -45.26% (p=0.000 n=8+7) name old allocs/op new allocs/op delta CopyNSmall-10 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=8+8) CopyNLarge-10 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=8+8) For #57202 Change-Id: I2292226da9ba1dc09a2543f5d74fe5da06080d49 Reviewed-on: https://go-review.googlesource.com/c/go/+/456555 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Damien Neil <dneil@google.com> Reviewed-by: Thomas Austad <thomas.austad@gmail.com> Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com>
-rw-r--r--src/io/io.go30
-rw-r--r--src/net/http/server.go17
2 files changed, 15 insertions, 32 deletions
diff --git a/src/io/io.go b/src/io/io.go
index 630ab73b56..374e20bf8c 100644
--- a/src/io/io.go
+++ b/src/io/io.go
@@ -400,6 +400,13 @@ func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
return copyBuffer(dst, src, buf)
}
+var bufPool = sync.Pool{
+ New: func() any {
+ b := make([]byte, 32*1024)
+ return &b
+ },
+}
+
// copyBuffer is the actual implementation of Copy and CopyBuffer.
// if buf is nil, one is allocated.
func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
@@ -413,15 +420,9 @@ func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
return rt.ReadFrom(src)
}
if buf == nil {
- size := 32 * 1024
- if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
- if l.N < 1 {
- size = 1
- } else {
- size = int(l.N)
- }
- }
- buf = make([]byte, size)
+ bufp := bufPool.Get().(*[]byte)
+ defer bufPool.Put(bufp)
+ buf = *bufp
}
for {
nr, er := src.Read(buf)
@@ -637,21 +638,14 @@ func (discard) WriteString(s string) (int, error) {
return len(s), nil
}
-var blackHolePool = sync.Pool{
- New: func() any {
- b := make([]byte, 8192)
- return &b
- },
-}
-
func (discard) ReadFrom(r Reader) (n int64, err error) {
- bufp := blackHolePool.Get().(*[]byte)
+ bufp := bufPool.Get().(*[]byte)
readSize := 0
for {
readSize, err = r.Read(*bufp)
n += int64(readSize)
if err != nil {
- blackHolePool.Put(bufp)
+ bufPool.Put(bufp)
if err == EOF {
return n, nil
}
diff --git a/src/net/http/server.go b/src/net/http/server.go
index c15f0f58cb..bb31761ade 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -567,16 +567,12 @@ type writerOnly struct {
// to a *net.TCPConn with sendfile, or from a supported src type such
// as a *net.TCPConn on Linux with splice.
func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
- bufp := copyBufPool.Get().(*[]byte)
- buf := *bufp
- defer copyBufPool.Put(bufp)
-
// Our underlying w.conn.rwc is usually a *TCPConn (with its
// own ReadFrom method). If not, just fall back to the normal
// copy method.
rf, ok := w.conn.rwc.(io.ReaderFrom)
if !ok {
- return io.CopyBuffer(writerOnly{w}, src, buf)
+ return io.Copy(writerOnly{w}, src)
}
// Copy the first sniffLen bytes before switching to ReadFrom.
@@ -584,7 +580,7 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
// source is available (see golang.org/issue/5660) and provides
// enough bytes to perform Content-Type sniffing when required.
if !w.cw.wroteHeader {
- n0, err := io.CopyBuffer(writerOnly{w}, io.LimitReader(src, sniffLen), buf)
+ n0, err := io.Copy(writerOnly{w}, io.LimitReader(src, sniffLen))
n += n0
if err != nil || n0 < sniffLen {
return n, err
@@ -602,7 +598,7 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
return n, err
}
- n0, err := io.CopyBuffer(writerOnly{w}, src, buf)
+ n0, err := io.Copy(writerOnly{w}, src)
n += n0
return n, err
}
@@ -799,13 +795,6 @@ var (
bufioWriter4kPool sync.Pool
)
-var copyBufPool = sync.Pool{
- New: func() any {
- b := make([]byte, 32*1024)
- return &b
- },
-}
-
func bufioWriterPool(size int) *sync.Pool {
switch size {
case 2 << 10: