mirror of
https://github.com/golang/go
synced 2024-11-22 22:10:03 -07:00
bufio: reuse Writer buffers after Flush
A bufio.Writer.Flush marks the usual end of a Writer's life. Recycle its internal buffer on those explicit flushes, but not on normal, as-needed internal flushes. benchmark old ns/op new ns/op delta BenchmarkWriterEmpty 1959 727 -62.89% benchmark old allocs new allocs delta BenchmarkWriterEmpty 2 1 -50.00% benchmark old bytes new bytes delta BenchmarkWriterEmpty 4215 83 -98.03% R=gri, iant CC=gobot, golang-dev, voidlogic7 https://golang.org/cl/9459044
This commit is contained in:
parent
ddda7980c5
commit
99f6722860
@ -29,7 +29,7 @@ var (
|
|||||||
|
|
||||||
// Reader implements buffering for an io.Reader object.
|
// Reader implements buffering for an io.Reader object.
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
buf []byte // either nil or []byte of size bufSize
|
buf []byte // either nil or []byte of length bufSize
|
||||||
bufSize int
|
bufSize int
|
||||||
rd io.Reader
|
rd io.Reader
|
||||||
r, w int
|
r, w int
|
||||||
@ -314,7 +314,7 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Buffer is full?
|
// Buffer is full?
|
||||||
if b.Buffered() >= len(b.buf) {
|
if b.Buffered() >= b.bufSize {
|
||||||
b.r = b.w
|
b.r = b.w
|
||||||
return b.buf, ErrBufferFull
|
return b.buf, ErrBufferFull
|
||||||
}
|
}
|
||||||
@ -473,10 +473,11 @@ func (b *Reader) writeBuf(w io.Writer) (int64, error) {
|
|||||||
// If an error occurs writing to a Writer, no more data will be
|
// If an error occurs writing to a Writer, no more data will be
|
||||||
// accepted and all subsequent writes will return the error.
|
// accepted and all subsequent writes will return the error.
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
err error
|
err error
|
||||||
buf []byte
|
buf []byte // either nil or []byte of length bufSize
|
||||||
n int
|
bufSize int
|
||||||
wr io.Writer
|
n int
|
||||||
|
wr io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWriterSize returns a new Writer whose buffer has at least the specified
|
// NewWriterSize returns a new Writer whose buffer has at least the specified
|
||||||
@ -485,16 +486,20 @@ type Writer struct {
|
|||||||
func NewWriterSize(wr io.Writer, size int) *Writer {
|
func NewWriterSize(wr io.Writer, size int) *Writer {
|
||||||
// Is it already a Writer?
|
// Is it already a Writer?
|
||||||
b, ok := wr.(*Writer)
|
b, ok := wr.(*Writer)
|
||||||
if ok && len(b.buf) >= size {
|
if ok && b.bufSize >= size {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
if size <= 0 {
|
if size <= 0 {
|
||||||
size = defaultBufSize
|
size = defaultBufSize
|
||||||
}
|
}
|
||||||
b = new(Writer)
|
b = &Writer{
|
||||||
// TODO(bradfitz): make Writer buffers lazy too, like Reader's
|
wr: wr,
|
||||||
b.buf = make([]byte, size)
|
bufSize: size,
|
||||||
b.wr = wr
|
}
|
||||||
|
if size > defaultBufSize {
|
||||||
|
// TODO(bradfitz): make all buffer sizes recycle
|
||||||
|
b.buf = make([]byte, b.bufSize)
|
||||||
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,8 +508,38 @@ func NewWriter(wr io.Writer) *Writer {
|
|||||||
return NewWriterSize(wr, defaultBufSize)
|
return NewWriterSize(wr, defaultBufSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allocBuf makes b.buf non-nil.
|
||||||
|
func (b *Writer) allocBuf() {
|
||||||
|
if b.buf != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case b.buf = <-bufCache:
|
||||||
|
b.buf = b.buf[:b.bufSize]
|
||||||
|
default:
|
||||||
|
b.buf = make([]byte, b.bufSize, defaultBufSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// putBuf returns b.buf if it's unused.
|
||||||
|
func (b *Writer) putBuf() {
|
||||||
|
if b.n == 0 && cap(b.buf) == defaultBufSize {
|
||||||
|
select {
|
||||||
|
case bufCache <- b.buf:
|
||||||
|
b.buf = nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Flush writes any buffered data to the underlying io.Writer.
|
// Flush writes any buffered data to the underlying io.Writer.
|
||||||
func (b *Writer) Flush() error {
|
func (b *Writer) Flush() error {
|
||||||
|
err := b.flush()
|
||||||
|
b.putBuf()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Writer) flush() error {
|
||||||
if b.err != nil {
|
if b.err != nil {
|
||||||
return b.err
|
return b.err
|
||||||
}
|
}
|
||||||
@ -528,7 +563,7 @@ func (b *Writer) Flush() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Available returns how many bytes are unused in the buffer.
|
// Available returns how many bytes are unused in the buffer.
|
||||||
func (b *Writer) Available() int { return len(b.buf) - b.n }
|
func (b *Writer) Available() int { return b.bufSize - b.n }
|
||||||
|
|
||||||
// Buffered returns the number of bytes that have been written into the current buffer.
|
// Buffered returns the number of bytes that have been written into the current buffer.
|
||||||
func (b *Writer) Buffered() int { return b.n }
|
func (b *Writer) Buffered() int { return b.n }
|
||||||
@ -538,6 +573,7 @@ func (b *Writer) Buffered() int { return b.n }
|
|||||||
// If nn < len(p), it also returns an error explaining
|
// If nn < len(p), it also returns an error explaining
|
||||||
// why the write is short.
|
// why the write is short.
|
||||||
func (b *Writer) Write(p []byte) (nn int, err error) {
|
func (b *Writer) Write(p []byte) (nn int, err error) {
|
||||||
|
b.allocBuf()
|
||||||
for len(p) > b.Available() && b.err == nil {
|
for len(p) > b.Available() && b.err == nil {
|
||||||
var n int
|
var n int
|
||||||
if b.Buffered() == 0 {
|
if b.Buffered() == 0 {
|
||||||
@ -547,7 +583,7 @@ func (b *Writer) Write(p []byte) (nn int, err error) {
|
|||||||
} else {
|
} else {
|
||||||
n = copy(b.buf[b.n:], p)
|
n = copy(b.buf[b.n:], p)
|
||||||
b.n += n
|
b.n += n
|
||||||
b.Flush()
|
b.flush()
|
||||||
}
|
}
|
||||||
nn += n
|
nn += n
|
||||||
p = p[n:]
|
p = p[n:]
|
||||||
@ -566,9 +602,12 @@ func (b *Writer) WriteByte(c byte) error {
|
|||||||
if b.err != nil {
|
if b.err != nil {
|
||||||
return b.err
|
return b.err
|
||||||
}
|
}
|
||||||
if b.Available() <= 0 && b.Flush() != nil {
|
if b.Available() <= 0 && b.flush() != nil {
|
||||||
return b.err
|
return b.err
|
||||||
}
|
}
|
||||||
|
if b.buf == nil {
|
||||||
|
b.allocBuf()
|
||||||
|
}
|
||||||
b.buf[b.n] = c
|
b.buf[b.n] = c
|
||||||
b.n++
|
b.n++
|
||||||
return nil
|
return nil
|
||||||
@ -577,6 +616,9 @@ func (b *Writer) WriteByte(c byte) error {
|
|||||||
// WriteRune writes a single Unicode code point, returning
|
// WriteRune writes a single Unicode code point, returning
|
||||||
// the number of bytes written and any error.
|
// the number of bytes written and any error.
|
||||||
func (b *Writer) WriteRune(r rune) (size int, err error) {
|
func (b *Writer) WriteRune(r rune) (size int, err error) {
|
||||||
|
if b.buf == nil {
|
||||||
|
b.allocBuf()
|
||||||
|
}
|
||||||
if r < utf8.RuneSelf {
|
if r < utf8.RuneSelf {
|
||||||
err = b.WriteByte(byte(r))
|
err = b.WriteByte(byte(r))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -589,7 +631,7 @@ func (b *Writer) WriteRune(r rune) (size int, err error) {
|
|||||||
}
|
}
|
||||||
n := b.Available()
|
n := b.Available()
|
||||||
if n < utf8.UTFMax {
|
if n < utf8.UTFMax {
|
||||||
if b.Flush(); b.err != nil {
|
if b.flush(); b.err != nil {
|
||||||
return 0, b.err
|
return 0, b.err
|
||||||
}
|
}
|
||||||
n = b.Available()
|
n = b.Available()
|
||||||
@ -608,13 +650,14 @@ func (b *Writer) WriteRune(r rune) (size int, err error) {
|
|||||||
// If the count is less than len(s), it also returns an error explaining
|
// If the count is less than len(s), it also returns an error explaining
|
||||||
// why the write is short.
|
// why the write is short.
|
||||||
func (b *Writer) WriteString(s string) (int, error) {
|
func (b *Writer) WriteString(s string) (int, error) {
|
||||||
|
b.allocBuf()
|
||||||
nn := 0
|
nn := 0
|
||||||
for len(s) > b.Available() && b.err == nil {
|
for len(s) > b.Available() && b.err == nil {
|
||||||
n := copy(b.buf[b.n:], s)
|
n := copy(b.buf[b.n:], s)
|
||||||
b.n += n
|
b.n += n
|
||||||
nn += n
|
nn += n
|
||||||
s = s[n:]
|
s = s[n:]
|
||||||
b.Flush()
|
b.flush()
|
||||||
}
|
}
|
||||||
if b.err != nil {
|
if b.err != nil {
|
||||||
return nn, b.err
|
return nn, b.err
|
||||||
@ -627,6 +670,7 @@ func (b *Writer) WriteString(s string) (int, error) {
|
|||||||
|
|
||||||
// ReadFrom implements io.ReaderFrom.
|
// ReadFrom implements io.ReaderFrom.
|
||||||
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
|
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
b.allocBuf()
|
||||||
if b.Buffered() == 0 {
|
if b.Buffered() == 0 {
|
||||||
if w, ok := b.wr.(io.ReaderFrom); ok {
|
if w, ok := b.wr.(io.ReaderFrom); ok {
|
||||||
return w.ReadFrom(r)
|
return w.ReadFrom(r)
|
||||||
@ -641,7 +685,7 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
b.n += m
|
b.n += m
|
||||||
n += int64(m)
|
n += int64(m)
|
||||||
if b.Available() == 0 {
|
if b.Available() == 0 {
|
||||||
if err1 := b.Flush(); err1 != nil {
|
if err1 := b.flush(); err1 != nil {
|
||||||
return n, err1
|
return n, err1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1098,3 +1098,31 @@ func BenchmarkReaderEmpty(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkWriterEmpty(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
str := strings.Repeat("x", 1<<10)
|
||||||
|
bs := []byte(str)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
bw := NewWriter(ioutil.Discard)
|
||||||
|
bw.Flush()
|
||||||
|
bw.WriteByte('a')
|
||||||
|
bw.Flush()
|
||||||
|
bw.WriteRune('B')
|
||||||
|
bw.Flush()
|
||||||
|
bw.Write(bs)
|
||||||
|
bw.Flush()
|
||||||
|
bw.WriteString(str)
|
||||||
|
bw.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkWriterFlush(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
bw := NewWriter(ioutil.Discard)
|
||||||
|
str := strings.Repeat("x", 50)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
bw.WriteString(str)
|
||||||
|
bw.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user