Skip to content

Commit 6fd82d8

Browse files
artyombradfitz
authored andcommitted
net/http: optimize some io.Copy calls by reusing buffers
Optimize two calls of io.Copy which cannot make use of neither io.ReaderFrom nor io.WriterTo optimization tricks by replacing them with io.CopyBuffer with reusable buffers. First is fallback call to io.Copy when server misses the optimized case of using sendfile to copy from a regular file to net.TCPConn; second is use of io.Copy on piped reader/writer when handler implementation uses http.CloseNotifier interface. One of the notable users of http.CloseNotifier is httputil.ReverseProxy. benchmark old ns/op new ns/op delta BenchmarkCloseNotifier-4 309591 303388 -2.00% benchmark old allocs new allocs delta BenchmarkCloseNotifier-4 50 49 -2.00% benchmark old bytes new bytes delta BenchmarkCloseNotifier-4 36168 3140 -91.32% Fixes #12455 Change-Id: I512e6aa2f1aeed2ed00246afb3350c819b65b87e Reviewed-on: https://go-review.googlesource.com/14177 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 8c2c35d commit 6fd82d8

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

src/net/http/serve_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -3685,3 +3685,35 @@ Host: golang.org
36853685
<-conn.closec
36863686
}
36873687
}
3688+
3689+
func BenchmarkCloseNotifier(b *testing.B) {
3690+
b.ReportAllocs()
3691+
b.StopTimer()
3692+
sawClose := make(chan bool)
3693+
ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
3694+
<-rw.(CloseNotifier).CloseNotify()
3695+
sawClose <- true
3696+
}))
3697+
defer ts.Close()
3698+
tot := time.NewTimer(5 * time.Second)
3699+
defer tot.Stop()
3700+
b.StartTimer()
3701+
for i := 0; i < b.N; i++ {
3702+
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
3703+
if err != nil {
3704+
b.Fatalf("error dialing: %v", err)
3705+
}
3706+
_, err = fmt.Fprintf(conn, "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n")
3707+
if err != nil {
3708+
b.Fatal(err)
3709+
}
3710+
conn.Close()
3711+
tot.Reset(5 * time.Second)
3712+
select {
3713+
case <-sawClose:
3714+
case <-tot.C:
3715+
b.Fatal("timeout")
3716+
}
3717+
}
3718+
b.StopTimer()
3719+
}

src/net/http/server.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,9 @@ func (c *conn) closeNotify() <-chan bool {
179179
c.sr.r = pr
180180
c.sr.Unlock()
181181
go func() {
182-
_, err := io.Copy(pw, readSource)
182+
bufp := copyBufPool.Get().(*[]byte)
183+
defer copyBufPool.Put(bufp)
184+
_, err := io.CopyBuffer(pw, readSource, *bufp)
183185
if err == nil {
184186
err = io.EOF
185187
}
@@ -423,7 +425,9 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
423425
return 0, err
424426
}
425427
if !ok || !regFile {
426-
return io.Copy(writerOnly{w}, src)
428+
bufp := copyBufPool.Get().(*[]byte)
429+
defer copyBufPool.Put(bufp)
430+
return io.CopyBuffer(writerOnly{w}, src, *bufp)
427431
}
428432

429433
// sendfile path:
@@ -487,6 +491,13 @@ var (
487491
bufioWriter4kPool sync.Pool
488492
)
489493

494+
var copyBufPool = sync.Pool{
495+
New: func() interface{} {
496+
b := make([]byte, 32*1024)
497+
return &b
498+
},
499+
}
500+
490501
func bufioWriterPool(size int) *sync.Pool {
491502
switch size {
492503
case 2 << 10:

0 commit comments

Comments
 (0)