1
0
mirror of https://github.com/golang/go synced 2024-11-26 16:16:57 -07:00

net/http: reuse bufio.Reader and bufio.Writer between conns

Saves over 8KB of allocations per new connection.

benchmark                             old ns/op    new ns/op    delta
BenchmarkServerFakeConnNoKeepAlive        28777        24927  -13.38%

benchmark                            old allocs   new allocs    delta
BenchmarkServerFakeConnNoKeepAlive           52           46  -11.54%

benchmark                             old bytes    new bytes    delta
BenchmarkServerFakeConnNoKeepAlive        13716         5286  -61.46%

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/7799047
This commit is contained in:
Brad Fitzpatrick 2013-03-21 20:02:01 -07:00
parent dc71ace282
commit 985b0992cd

View File

@ -109,9 +109,11 @@ type conn struct {
remoteAddr string // network address of remote side remoteAddr string // network address of remote side
server *Server // the Server on which the connection arrived server *Server // the Server on which the connection arrived
rwc net.Conn // i/o connection rwc net.Conn // i/o connection
sr switchReader // where the LimitReader reads from; usually the rwc sr liveSwitchReader // where the LimitReader reads from; usually the rwc
lr *io.LimitedReader // io.LimitReader(sr) lr *io.LimitedReader // io.LimitReader(sr)
buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc
bufswr *switchReader // the *switchReader io.Reader source of buf
bufsww *switchWriter // the *switchWriter io.Writer dest of buf
tlsState *tls.ConnectionState // or nil when not using TLS tlsState *tls.ConnectionState // or nil when not using TLS
mu sync.Mutex // guards the following mu sync.Mutex // guards the following
@ -180,12 +182,26 @@ func (c *conn) noteClientGone() {
c.clientGone = true c.clientGone = true
} }
// A switchReader can have its Reader changed at runtime.
// It's not safe for concurrent Reads and switches.
type switchReader struct { type switchReader struct {
io.Reader
}
// A switchWriter can have its Writer changed at runtime.
// It's not safe for concurrent Writes and switches.
type switchWriter struct {
io.Writer
}
// A liveSwitchReader is a switchReader that's safe for concurrent
// reads and switches, if its mutex is held.
type liveSwitchReader struct {
sync.Mutex sync.Mutex
r io.Reader r io.Reader
} }
func (sr *switchReader) Read(p []byte) (n int, err error) { func (sr *liveSwitchReader) Read(p []byte) (n int, err error) {
sr.Lock() sr.Lock()
r := sr.r r := sr.r
sr.Unlock() sr.Unlock()
@ -362,14 +378,87 @@ func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
if debugServerConnections { if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc) c.rwc = newLoggingConn("server", c.rwc)
} }
c.sr = switchReader{r: c.rwc} c.sr = liveSwitchReader{r: c.rwc}
c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader) c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader)
br := bufio.NewReader(c.lr) br, sr := newBufioReader(c.lr)
bw := bufio.NewWriter(c.rwc) bw, sw := newBufioWriter(c.rwc)
c.buf = bufio.NewReadWriter(br, bw) c.buf = bufio.NewReadWriter(br, bw)
c.bufswr = sr
c.bufsww = sw
return c, nil return c, nil
} }
// TODO: remove this, if issue 5100 is fixed
type bufioReaderPair struct {
br *bufio.Reader
sr *switchReader // from which the bufio.Reader is reading
}
// TODO: remove this, if issue 5100 is fixed
type bufioWriterPair struct {
bw *bufio.Writer
sw *switchWriter // to which the bufio.Writer is writing
}
// TODO: use a sync.Cache instead
var (
bufioReaderCache = make(chan bufioReaderPair, 4)
bufioWriterCache = make(chan bufioWriterPair, 4)
)
func newBufioReader(r io.Reader) (*bufio.Reader, *switchReader) {
select {
case p := <-bufioReaderCache:
p.sr.Reader = r
return p.br, p.sr
default:
sr := &switchReader{r}
return bufio.NewReader(sr), sr
}
}
func putBufioReader(br *bufio.Reader, sr *switchReader) {
if n := br.Buffered(); n > 0 {
io.CopyN(ioutil.Discard, br, int64(n))
}
br.Read(nil) // clears br.err
sr.Reader = nil
select {
case bufioReaderCache <- bufioReaderPair{br, sr}:
default:
}
}
func newBufioWriter(w io.Writer) (*bufio.Writer, *switchWriter) {
select {
case p := <-bufioWriterCache:
p.sw.Writer = w
return p.bw, p.sw
default:
sw := &switchWriter{w}
return bufio.NewWriter(sw), sw
}
}
func putBufioWriter(bw *bufio.Writer, sw *switchWriter) {
if bw.Buffered() > 0 {
// It must have failed to flush to its target
// earlier. We can't reuse this bufio.Writer.
return
}
if err := bw.Flush(); err != nil {
// Its sticky error field is set, which is returned by
// Flush even when there's no data buffered. This
// bufio Writer is dead to us. Don't reuse it.
return
}
sw.Writer = nil
select {
case bufioWriterCache <- bufioWriterPair{bw, sw}:
default:
}
}
// DefaultMaxHeaderBytes is the maximum permitted size of the headers // DefaultMaxHeaderBytes is the maximum permitted size of the headers
// in an HTTP request. // in an HTTP request.
// This can be overridden by setting Server.MaxHeaderBytes. // This can be overridden by setting Server.MaxHeaderBytes.
@ -742,6 +831,15 @@ func (w *response) Flush() {
func (c *conn) finalFlush() { func (c *conn) finalFlush() {
if c.buf != nil { if c.buf != nil {
c.buf.Flush() c.buf.Flush()
// Steal the bufio.Reader (~4KB worth of memory) and its associated
// reader for a future connection.
putBufioReader(c.buf.Reader, c.bufswr)
// Steal the bufio.Writer (~4KB worth of memory) and its associated
// writer for a future connection.
putBufioWriter(c.buf.Writer, c.bufsww)
c.buf = nil c.buf = nil
} }
} }