-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace byte.Buffer usage with sys.ByteBuffer which is faster for the operations we need. ``` BenchmarkByteBuffer/byteBuffer-4 200000000 8.89 ns/op 0 B/op 0 allocs/op BenchmarkByteBuffer/bytes.Buffer-4 50000000 28.0 ns/op 0 B/op 0 allocs/op BenchmarkByteBufferGrow/byteBuffer-4 100000000 15.7 ns/op 2 B/op 0 allocs/op BenchmarkByteBufferGrow/bytes.Buffer-4 100000000 24.1 ns/op 2 B/op 0 allocs/op ```
- Loading branch information
1 parent
516a307
commit b0f98f6
Showing
3 changed files
with
150 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package sys | ||
|
||
// ByteBuffer is an expandable buffer backed by a byte slice. | ||
type ByteBuffer struct { | ||
buf []byte | ||
offset int | ||
} | ||
|
||
// NewByteBuffer creates a new ByteBuffer with an initial capacity of | ||
// initialSize. | ||
func NewByteBuffer(initialSize int) *ByteBuffer { | ||
return &ByteBuffer{buf: make([]byte, initialSize)} | ||
} | ||
|
||
// Write appends the contents of p to the buffer, growing the buffer as needed. | ||
// The return value is the length of p; err is always nil. | ||
func (b *ByteBuffer) Write(p []byte) (int, error) { | ||
if len(b.buf) < b.offset+len(p) { | ||
// Create a buffer larger than needed so we don't spend lots of time | ||
// allocating and copying. | ||
spaceNeeded := len(b.buf) - b.offset + len(p) | ||
largerBuf := make([]byte, 2*len(b.buf)+spaceNeeded) | ||
copy(largerBuf, b.buf[:b.offset]) | ||
b.buf = largerBuf | ||
} | ||
n := copy(b.buf[b.offset:], p) | ||
b.offset += n | ||
return n, nil | ||
} | ||
|
||
// Reset resets the buffer to be empty. It retains the same underlying storage. | ||
func (b *ByteBuffer) Reset() { | ||
b.offset = 0 | ||
b.buf = b.buf[:cap(b.buf)] | ||
} | ||
|
||
// Bytes returns a slice of length b.Len() holding the bytes that have been | ||
// written to the buffer. | ||
func (b *ByteBuffer) Bytes() []byte { | ||
return b.buf[:b.offset] | ||
} | ||
|
||
// Len returns the number of bytes that have been written to the buffer. | ||
func (b *ByteBuffer) Len() int { | ||
return b.offset | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package sys | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
var _ io.Writer = &ByteBuffer{} | ||
|
||
func TestByteBuffer(t *testing.T) { | ||
input := "hello" | ||
length := len(input) | ||
buf := NewByteBuffer(1024) | ||
|
||
n, err := buf.Write([]byte(input)) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
assert.Equal(t, length, n) | ||
|
||
assert.Equal(t, input, string(buf.Bytes())) | ||
assert.Equal(t, length, len(buf.Bytes())) | ||
assert.Equal(t, length, buf.Len()) | ||
} | ||
|
||
func TestByteBufferGrow(t *testing.T) { | ||
input := "hello" | ||
length := len(input) | ||
buf := NewByteBuffer(0) | ||
|
||
n, err := buf.Write([]byte(input)) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
assert.Equal(t, length, n) | ||
|
||
assert.Equal(t, input, string(buf.Bytes())) | ||
assert.Equal(t, length, len(buf.Bytes())) | ||
assert.Equal(t, length, buf.Len()) | ||
assert.Equal(t, length, len(buf.buf)) | ||
|
||
n, err = buf.Write([]byte(input)) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
assert.Equal(t, length, n) | ||
|
||
assert.Equal(t, input+input, string(buf.Bytes())) | ||
assert.Equal(t, 2*length, len(buf.Bytes())) | ||
assert.Equal(t, 2*length, buf.Len()) | ||
} | ||
|
||
func BenchmarkByteBuffer(b *testing.B) { | ||
input := []byte("test writing this sentence to a buffer") | ||
|
||
b.Run("byteBuffer", func(b *testing.B) { | ||
buf := NewByteBuffer(1024) | ||
b.ResetTimer() | ||
|
||
for i := 0; i < b.N; i++ { | ||
buf.Write(input) | ||
buf.Bytes() | ||
buf.Reset() | ||
} | ||
}) | ||
|
||
b.Run("bytes.Buffer", func(b *testing.B) { | ||
buf := bytes.NewBuffer(make([]byte, 0, 1024)) | ||
b.ResetTimer() | ||
|
||
for i := 0; i < b.N; i++ { | ||
buf.Write(input) | ||
buf.Bytes() | ||
buf.Reset() | ||
} | ||
}) | ||
} | ||
|
||
func BenchmarkByteBufferGrow(b *testing.B) { | ||
b.Run("byteBuffer", func(b *testing.B) { | ||
buf := NewByteBuffer(0) | ||
b.ResetTimer() | ||
|
||
for i := 0; i < b.N; i++ { | ||
buf.Write([]byte("a")) | ||
buf.Bytes() | ||
} | ||
}) | ||
|
||
b.Run("bytes.Buffer", func(b *testing.B) { | ||
buf := bytes.NewBuffer(make([]byte, 0)) | ||
b.ResetTimer() | ||
|
||
for i := 0; i < b.N; i++ { | ||
buf.Write([]byte("a")) | ||
buf.Bytes() | ||
} | ||
}) | ||
} |