-
Notifications
You must be signed in to change notification settings - Fork 230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Buffer optimizations #355
Buffer optimizations #355
Conversation
Pull Request Test Coverage Report for Build 7344840336
💛 - Coveralls |
This looks good to my eyes, but would like to confirm with @werbenhu before we merge |
@thedevop I added two benchmark test cases with different results. This scenario should be closer to our use case, where we are writing from one buffer to another. Take a look at the test results. func BenchmarkWrite(b *testing.B) {
const PacketSize = 128
d := make([]byte, PacketSize)
if _, err := rand.Read(d); err != nil {
panic(err)
}
dst := new(bytes.Buffer)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
src := bytes.NewBuffer(d)
dst.Write(src.Bytes())
dst.Reset()
}
}
func BenchmarkWriteTo(b *testing.B) {
const PacketSize = 128
d := make([]byte, PacketSize)
if _, err := rand.Read(d); err != nil {
panic(err)
}
dst := new(bytes.Buffer)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
src := bytes.NewBuffer(d)
src.WriteTo(dst)
dst.Reset()
}
}
func BenchmarkWrite2(b *testing.B) {
const PacketSize = 128
d := make([]byte, PacketSize)
if _, err := rand.Read(d); err != nil {
panic(err)
}
dst := new(bytes.Buffer)
src := bytes.NewBuffer(d)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
dst.Write(src.Bytes())
dst.Reset()
}
}
func BenchmarkWriteTo2(b *testing.B) {
const PacketSize = 128
d := make([]byte, PacketSize)
if _, err := rand.Read(d); err != nil {
panic(err)
}
dst := new(bytes.Buffer)
src := bytes.NewBuffer(d)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
src.WriteTo(dst)
dst.Reset()
}
}
|
@werbenhu , Buffer once is read, it will no longer be available to read again. Hence for i := 0; i < b.N; i++ {
src.WriteTo(dst)
dst.Reset()
} will only write its content to dst the 1st time, subsequent writes within the for loop will be a 0 byte write. That's the reason in the original benchmark the src is reinitialize within the for loop. Also, if we look at the source for Buffer.WriteTo, it actually calls the dst.Write with some additional checks and error handling. So func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
b.lastRead = opInvalid
if nBytes := b.Len(); nBytes > 0 {
m, e := w.Write(b.buf[b.off:])
if m > nBytes {
panic("bytes.Buffer.WriteTo: invalid Write count")
}
b.off += m
n = int64(m)
if e != nil {
return n, e
}
// all bytes should have been written, by definition of
// Write method in io.Writer
if m != nBytes {
return n, io.ErrShortWrite
}
}
// Buffer is now empty; reset.
b.Reset()
return n, nil
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thedevop You are right; it does look good.
Few buffer optimizations:
Benchmark comparing Write vs WriteTo (the performance difference is reverse proportion to the packet size, using 128 bytes for benchmark as vast majority packets without payload should be less than that):