bytes: improve WriteRune performance

Remove the runeBytes buffer and write the utf8 encoding directly
to the internal buf byte slice.

name         old time/op   new time/op   delta
WriteRune-4   80.5µs ± 2%   57.1µs ± 2%  -29.06%  (p=0.000 n=20+20)

name         old speed     new speed     delta
WriteRune-4  153MB/s ± 2%  215MB/s ± 2%  +40.96%  (p=0.000 n=20+20)

Change-Id: Ic15f6e2d6e56a3d15c74f56159e2eae020ba73ba
Reviewed-on: https://go-review.googlesource.com/28816
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Martin Möhrmann 2016-09-07 08:59:00 +02:00 committed by Brad Fitzpatrick
parent 07bcc16547
commit 2321895fe2
2 changed files with 21 additions and 7 deletions

View file

@ -15,11 +15,10 @@ import (
// A Buffer is a variable-sized buffer of bytes with Read and Write methods. // A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use. // The zero value for Buffer is an empty buffer ready to use.
type Buffer struct { type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)] buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)] off int // read at &buf[off], write at &buf[len(buf)]
runeBytes [utf8.UTFMax]byte // avoid allocation of slice on each call to WriteRune bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation. lastRead readOp // last read operation, so that Unread* can work correctly.
lastRead readOp // last read operation, so that Unread* can work correctly.
} }
// The readOp constants describe the last action performed on // The readOp constants describe the last action performed on
@ -246,8 +245,10 @@ func (b *Buffer) WriteRune(r rune) (n int, err error) {
b.WriteByte(byte(r)) b.WriteByte(byte(r))
return 1, nil return 1, nil
} }
n = utf8.EncodeRune(b.runeBytes[0:], r) b.lastRead = opInvalid
b.Write(b.runeBytes[0:n]) m := b.grow(utf8.UTFMax)
n = utf8.EncodeRune(b.buf[m:m+utf8.UTFMax], r)
b.buf = b.buf[:m+n]
return n, nil return n, nil
} }

View file

@ -514,6 +514,19 @@ func TestBufferGrowth(t *testing.T) {
} }
} }
func BenchmarkWriteRune(b *testing.B) {
const n = 4 << 10
const r = '☺'
b.SetBytes(int64(n * utf8.RuneLen(r)))
buf := NewBuffer(make([]byte, n*utf8.UTFMax))
for i := 0; i < b.N; i++ {
buf.Reset()
for i := 0; i < n; i++ {
buf.WriteRune(r)
}
}
}
// From Issue 5154. // From Issue 5154.
func BenchmarkBufferNotEmptyWriteRead(b *testing.B) { func BenchmarkBufferNotEmptyWriteRead(b *testing.B) {
buf := make([]byte, 1024) buf := make([]byte, 1024)