Skip to content

Commit 3627305

Browse files
committed
Revert "remove allocator"
This reverts commit f893f03.
1 parent f893f03 commit 3627305

File tree

4 files changed

+154
-1
lines changed

4 files changed

+154
-1
lines changed

alloc.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package smux
2+
3+
import (
4+
"sync"
5+
6+
"github.com/pkg/errors"
7+
)
8+
9+
var defaultAllocator *Allocator
10+
11+
func init() {
12+
defaultAllocator = NewAllocator()
13+
}
14+
15+
// Allocator for incoming frames, optimized to prevent overwriting after zeroing
16+
type Allocator struct {
17+
buffers []sync.Pool
18+
}
19+
20+
// NewAllocator initiates a []byte allocator for frames less than 65536 bytes,
21+
// the waste(memory fragmentation) of space allocation is guaranteed to be
22+
// no more than 50%.
23+
func NewAllocator() *Allocator {
24+
alloc := new(Allocator)
25+
alloc.buffers = make([]sync.Pool, 17) // 1B -> 64K
26+
for k := range alloc.buffers {
27+
i := k
28+
alloc.buffers[k].New = func() interface{} {
29+
return make([]byte, 1<<uint32(i))
30+
}
31+
}
32+
return alloc
33+
}
34+
35+
// Get a []byte from pool with most appropriate cap
36+
func (alloc *Allocator) Get(size int) []byte {
37+
if size <= 0 || size > 65536 {
38+
return nil
39+
}
40+
41+
bits := msb(size)
42+
if size == 1<<bits {
43+
return alloc.buffers[bits].Get().([]byte)[:size]
44+
} else {
45+
return alloc.buffers[bits+1].Get().([]byte)[:size]
46+
}
47+
}
48+
49+
// Put returns a []byte to pool for future use,
50+
// which the cap must be exactly 2^n
51+
func (alloc *Allocator) Put(buf []byte) error {
52+
bits := msb(cap(buf))
53+
if cap(buf) == 0 || cap(buf) > 65536 || cap(buf) != 1<<bits {
54+
return errors.New("allocator Put() incorrect buffer size")
55+
}
56+
alloc.buffers[bits].Put(buf)
57+
return nil
58+
}
59+
60+
// msb return the pos of most significiant bit
61+
func msb(size int) uint16 {
62+
var pos uint16
63+
size >>= 1
64+
for size > 0 {
65+
size >>= 1
66+
pos++
67+
}
68+
return pos
69+
}

alloc_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package smux
2+
3+
import (
4+
"math/rand"
5+
"testing"
6+
)
7+
8+
func TestAllocGet(t *testing.T) {
9+
alloc := NewAllocator()
10+
if alloc.Get(0) != nil {
11+
t.Fatal(0)
12+
}
13+
if len(alloc.Get(1)) != 1 {
14+
t.Fatal(1)
15+
}
16+
if len(alloc.Get(2)) != 2 {
17+
t.Fatal(2)
18+
}
19+
if len(alloc.Get(3)) != 3 || cap(alloc.Get(3)) != 4 {
20+
t.Fatal(3)
21+
}
22+
if len(alloc.Get(4)) != 4 {
23+
t.Fatal(4)
24+
}
25+
if len(alloc.Get(1023)) != 1023 || cap(alloc.Get(1023)) != 1024 {
26+
t.Fatal(1023)
27+
}
28+
if len(alloc.Get(1024)) != 1024 {
29+
t.Fatal(1024)
30+
}
31+
if len(alloc.Get(65536)) != 65536 {
32+
t.Fatal(65536)
33+
}
34+
if alloc.Get(65537) != nil {
35+
t.Fatal(65537)
36+
}
37+
}
38+
39+
func TestAllocPut(t *testing.T) {
40+
alloc := NewAllocator()
41+
if err := alloc.Put(nil); err == nil {
42+
t.Fatal("put nil misbehavior")
43+
}
44+
if err := alloc.Put(make([]byte, 3, 3)); err == nil {
45+
t.Fatal("put elem:3 []bytes misbehavior")
46+
}
47+
if err := alloc.Put(make([]byte, 4, 4)); err != nil {
48+
t.Fatal("put elem:4 []bytes misbehavior")
49+
}
50+
if err := alloc.Put(make([]byte, 1023, 1024)); err != nil {
51+
t.Fatal("put elem:1024 []bytes misbehavior")
52+
}
53+
if err := alloc.Put(make([]byte, 65536, 65536)); err != nil {
54+
t.Fatal("put elem:65536 []bytes misbehavior")
55+
}
56+
if err := alloc.Put(make([]byte, 65537, 65537)); err == nil {
57+
t.Fatal("put elem:65537 []bytes misbehavior")
58+
}
59+
}
60+
61+
func TestAllocPutThenGet(t *testing.T) {
62+
alloc := NewAllocator()
63+
data := alloc.Get(4)
64+
alloc.Put(data)
65+
newData := alloc.Get(4)
66+
if cap(data) != cap(newData) {
67+
t.Fatal("different cap while alloc.Get()")
68+
}
69+
}
70+
71+
func BenchmarkMSB(b *testing.B) {
72+
for i := 0; i < b.N; i++ {
73+
msb(rand.Int())
74+
}
75+
}

session.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ func (s *Session) recvLoop() {
340340
s.streamLock.Unlock()
341341
case cmdPSH:
342342
if hdr.Length() > 0 {
343-
newbuf := make([]byte, hdr.Length())
343+
newbuf := defaultAllocator.Get(int(hdr.Length()))
344344
if written, err := io.ReadFull(s.conn, newbuf); err == nil {
345345
s.streamLock.Lock()
346346
if stream, ok := s.streams[sid]; ok {

stream.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type Stream struct {
1616
sess *Session
1717

1818
buffers [][]byte
19+
heads [][]byte // slice heads kept for recycle
1920

2021
bufferLock sync.Mutex
2122
frameSize int
@@ -70,6 +71,9 @@ func (s *Stream) Read(b []byte) (n int, err error) {
7071
if len(s.buffers[0]) == 0 {
7172
s.buffers[0] = nil
7273
s.buffers = s.buffers[1:]
74+
// full recycle
75+
defaultAllocator.Put(s.heads[0])
76+
s.heads = s.heads[1:]
7377
}
7478
}
7579
s.bufferLock.Unlock()
@@ -93,11 +97,13 @@ func (s *Stream) WriteTo(w io.Writer) (n int64, err error) {
9397
if len(s.buffers) > 0 {
9498
buf = s.buffers[0]
9599
s.buffers = s.buffers[1:]
100+
s.heads = s.heads[1:]
96101
}
97102
s.bufferLock.Unlock()
98103

99104
if buf != nil {
100105
nw, ew := w.Write(buf)
106+
defaultAllocator.Put(buf)
101107
s.sess.returnTokens(len(buf))
102108
if nw > 0 {
103109
n += int64(nw)
@@ -256,6 +262,7 @@ func (s *Stream) RemoteAddr() net.Addr {
256262
func (s *Stream) pushBytes(buf []byte) (written int, err error) {
257263
s.bufferLock.Lock()
258264
s.buffers = append(s.buffers, buf)
265+
s.heads = append(s.heads, buf)
259266
s.bufferLock.Unlock()
260267
return
261268
}
@@ -265,8 +272,10 @@ func (s *Stream) recycleTokens() (n int) {
265272
s.bufferLock.Lock()
266273
for k := range s.buffers {
267274
n += len(s.buffers[k])
275+
defaultAllocator.Put(s.heads[k])
268276
}
269277
s.buffers = nil
278+
s.heads = nil
270279
s.bufferLock.Unlock()
271280
return
272281
}

0 commit comments

Comments
 (0)