Skip to content

Commit

Permalink
opt: shrink the ring-buffer for saving memory
Browse files Browse the repository at this point in the history
  • Loading branch information
panjf2000 committed Jan 23, 2021
1 parent 1af3f6c commit 14fd04a
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 20 deletions.
2 changes: 1 addition & 1 deletion eventloop_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type internalEventloop struct {
idx int // loop index in the server loops list
svr *server // server in loop
poller *netpoll.Poller // epoll or kqueue
packet []byte // read packet buffer
packet []byte // read packet buffer whose capacity is 64KB
connCount int32 // number of active connections in event-loop
connections map[int]*conn // loop connections fd -> conn
eventHandler EventHandler // user eventHandler
Expand Down
23 changes: 13 additions & 10 deletions ringbuffer/ring_buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,6 @@ func (r *RingBuffer) Shift(n int) {

if n < r.Length() {
r.r = (r.r + n) & r.mask
if r.r == r.w {
r.isEmpty = true
}
} else {
r.Reset()
}
Expand Down Expand Up @@ -154,7 +151,7 @@ func (r *RingBuffer) Read(p []byte) (n int, err error) {
copy(p, r.buf[r.r:r.r+n])
r.r += n
if r.r == r.w {
r.isEmpty = true
r.Reset()
}
return
}
Expand All @@ -174,7 +171,7 @@ func (r *RingBuffer) Read(p []byte) (n int, err error) {
}
r.r = (r.r + n) & r.mask
if r.r == r.w {
r.isEmpty = true
r.Reset()
}

return n, err
Expand All @@ -191,7 +188,7 @@ func (r *RingBuffer) ReadByte() (b byte, err error) {
r.r = 0
}
if r.r == r.w {
r.isEmpty = true
r.Reset()
}

return b, err
Expand Down Expand Up @@ -370,24 +367,30 @@ func (r *RingBuffer) IsEmpty() bool {

// Reset the read pointer and writer pointer to zero.
func (r *RingBuffer) Reset() {
r.r = 0
r.w = 0
r.isEmpty = true
r.r, r.w = 0, 0

// Shrink the internal buffer for saving memory.
newCap := r.size / 2
newBuf := make([]byte, newCap)
r.buf = newBuf
r.size = newCap
r.mask = newCap - 1
}

func (r *RingBuffer) malloc(cap int) {
var newCap int
if r.size == 0 && initSize >= cap {
if r.size == 0 && cap < initSize {
newCap = initSize
} else {
newCap = internal.CeilToPowerOfTwo(r.size + cap)
}
newBuf := make([]byte, newCap)
oldLen := r.Length()
_, _ = r.Read(newBuf)
r.buf = newBuf
r.r = 0
r.w = oldLen
r.size = newCap
r.mask = newCap - 1
r.buf = newBuf
}
41 changes: 32 additions & 9 deletions ringbuffer/ring_buffer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ func TestRingBuffer_Write(t *testing.T) {
}

rb.Reset()
size = rb.Cap()
// write 4 * 2 = 8 bytes
n, _ = rb.Write([]byte(strings.Repeat("abcd", 2)))
if n != 8 {
Expand Down Expand Up @@ -264,6 +265,7 @@ func TestRingBuffer_Read(t *testing.T) {

// write 16 bytes to read
_, _ = rb.Write([]byte(strings.Repeat("abcd", 4)))
// read all data from buffer, it will be shrunk from 64 to 32.
n, err = rb.Read(buf)
if err != nil {
t.Fatalf("read failed: %v", err)
Expand All @@ -274,15 +276,16 @@ func TestRingBuffer_Read(t *testing.T) {
if rb.Length() != 0 {
t.Fatalf("expect len 0 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r)
}
if rb.Free() != 64 {
if rb.Free() != 32 {
t.Fatalf("expect free 64 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r)
}
if rb.r != 16 {
t.Fatalf("expect r.r=16 but got %d. r.w=%d", rb.r, rb.w)
if rb.r != 0 {
t.Fatalf("expect r.r=0 but got %d. r.w=%d", rb.r, rb.w)
}

// write long slice to read, it will scale from 64 to 128 bytes.
// write long slice to read, it will scale from 32 to 128 bytes.
_, _ = rb.Write([]byte(strings.Repeat("abcd", 20)))
// read all data from buffer, it will be shrunk from 128 to 64.
n, err = rb.Read(buf)
if err != nil {
t.Fatalf("read failed: %v", err)
Expand All @@ -293,11 +296,11 @@ func TestRingBuffer_Read(t *testing.T) {
if rb.Length() != 0 {
t.Fatalf("expect len 0 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r)
}
if rb.Free() != 128 {
if rb.Free() != 64 {
t.Fatalf("expect free 128 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r)
}
if rb.r != 80 {
t.Fatalf("expect r.r=80 but got %d. r.w=%d", rb.r, rb.w)
if rb.r != 0 {
t.Fatalf("expect r.r=0 but got %d. r.w=%d", rb.r, rb.w)
}

rb.Reset()
Expand Down Expand Up @@ -374,7 +377,7 @@ func TestRingBuffer_ByteInterface(t *testing.T) {
t.Fatalf("expect IsFull is false but got true")
}

// write to, isFull
// write two, isFull
_ = rb.WriteByte('b')
if rb.Length() != 2 {
t.Fatalf("expect len 2 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r)
Expand Down Expand Up @@ -461,7 +464,7 @@ func TestRingBuffer_ByteInterface(t *testing.T) {
if rb.Length() != 0 {
t.Fatalf("expect len 0 byte but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r)
}
if rb.Free() != 4 {
if rb.Free() != 2 {
t.Fatalf("expect free 4 byte but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r)
}
// check empty or full
Expand All @@ -472,3 +475,23 @@ func TestRingBuffer_ByteInterface(t *testing.T) {
t.Fatalf("expect IsFull is false but got true")
}
}

func TestShrinkBuffer(t *testing.T) {
testStr := "Hello World!"
testCap := 1024

rb := New(testCap)
_, _ = rb.WriteString(testStr)
rb.LazyReadAll()
rb.Shift(len(testStr))
if rb.Cap() != testCap/2 {
t.Fatalf("expect buffer capacity %d, but got %d", testCap/2, rb.Cap())
}

_, _ = rb.WriteString(testStr)
rb.LazyReadAll()
rb.Reset()
if rb.Cap() != testCap/4 {
t.Fatalf("expect buffer capacity %d, but got %d", testCap/4, rb.Cap())
}
}

0 comments on commit 14fd04a

Please sign in to comment.