mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-03 11:57:39 +03:00
Pool allocate arrays instead of slices
This is inspired by https://go-review.googlesource.com/c/net/+/539915
This commit is contained in:
parent
d7ce998e7e
commit
f23499eaea
1 changed files with 104 additions and 16 deletions
|
@ -8,7 +8,7 @@ import (
|
|||
"sync"
|
||||
)
|
||||
|
||||
var DefaultAllocator = newDefaultAllocer()
|
||||
var DefaultAllocator = newDefaultAllocator()
|
||||
|
||||
type Allocator interface {
|
||||
Get(size int) []byte
|
||||
|
@ -17,22 +17,34 @@ type Allocator interface {
|
|||
|
||||
// defaultAllocator for incoming frames, optimized to prevent overwriting after zeroing
|
||||
type defaultAllocator struct {
|
||||
buffers []sync.Pool
|
||||
buffers [17]sync.Pool
|
||||
}
|
||||
|
||||
// NewAllocator initiates a []byte allocator for frames less than 65536 bytes,
|
||||
// the waste(memory fragmentation) of space allocation is guaranteed to be
|
||||
// no more than 50%.
|
||||
func newDefaultAllocer() Allocator {
|
||||
alloc := new(defaultAllocator)
|
||||
alloc.buffers = make([]sync.Pool, 17) // 1B -> 64K
|
||||
for k := range alloc.buffers {
|
||||
i := k
|
||||
alloc.buffers[k].New = func() any {
|
||||
return make([]byte, 1<<uint32(i))
|
||||
}
|
||||
func newDefaultAllocator() Allocator {
|
||||
return &defaultAllocator{
|
||||
buffers: [...]sync.Pool{ // 1B -> 64K
|
||||
{New: func() any { return new([1]byte) }},
|
||||
{New: func() any { return new([1 << 1]byte) }},
|
||||
{New: func() any { return new([1 << 2]byte) }},
|
||||
{New: func() any { return new([1 << 3]byte) }},
|
||||
{New: func() any { return new([1 << 4]byte) }},
|
||||
{New: func() any { return new([1 << 5]byte) }},
|
||||
{New: func() any { return new([1 << 6]byte) }},
|
||||
{New: func() any { return new([1 << 7]byte) }},
|
||||
{New: func() any { return new([1 << 8]byte) }},
|
||||
{New: func() any { return new([1 << 9]byte) }},
|
||||
{New: func() any { return new([1 << 10]byte) }},
|
||||
{New: func() any { return new([1 << 11]byte) }},
|
||||
{New: func() any { return new([1 << 12]byte) }},
|
||||
{New: func() any { return new([1 << 13]byte) }},
|
||||
{New: func() any { return new([1 << 14]byte) }},
|
||||
{New: func() any { return new([1 << 15]byte) }},
|
||||
{New: func() any { return new([1 << 16]byte) }},
|
||||
},
|
||||
}
|
||||
return alloc
|
||||
}
|
||||
|
||||
// Get a []byte from pool with most appropriate cap
|
||||
|
@ -41,12 +53,50 @@ func (alloc *defaultAllocator) Get(size int) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
bits := msb(size)
|
||||
if size == 1<<bits {
|
||||
return alloc.buffers[bits].Get().([]byte)[:size]
|
||||
index := msb(size)
|
||||
if size != 1<<index {
|
||||
index += 1
|
||||
}
|
||||
|
||||
return alloc.buffers[bits+1].Get().([]byte)[:size]
|
||||
buffer := alloc.buffers[index].Get()
|
||||
switch index {
|
||||
case 0:
|
||||
return buffer.(*[1]byte)[:size]
|
||||
case 1:
|
||||
return buffer.(*[1 << 1]byte)[:size]
|
||||
case 2:
|
||||
return buffer.(*[1 << 2]byte)[:size]
|
||||
case 3:
|
||||
return buffer.(*[1 << 3]byte)[:size]
|
||||
case 4:
|
||||
return buffer.(*[1 << 4]byte)[:size]
|
||||
case 5:
|
||||
return buffer.(*[1 << 5]byte)[:size]
|
||||
case 6:
|
||||
return buffer.(*[1 << 6]byte)[:size]
|
||||
case 7:
|
||||
return buffer.(*[1 << 7]byte)[:size]
|
||||
case 8:
|
||||
return buffer.(*[1 << 8]byte)[:size]
|
||||
case 9:
|
||||
return buffer.(*[1 << 9]byte)[:size]
|
||||
case 10:
|
||||
return buffer.(*[1 << 10]byte)[:size]
|
||||
case 11:
|
||||
return buffer.(*[1 << 11]byte)[:size]
|
||||
case 12:
|
||||
return buffer.(*[1 << 12]byte)[:size]
|
||||
case 13:
|
||||
return buffer.(*[1 << 13]byte)[:size]
|
||||
case 14:
|
||||
return buffer.(*[1 << 14]byte)[:size]
|
||||
case 15:
|
||||
return buffer.(*[1 << 15]byte)[:size]
|
||||
case 16:
|
||||
return buffer.(*[1 << 16]byte)[:size]
|
||||
default:
|
||||
panic("invalid pool index")
|
||||
}
|
||||
}
|
||||
|
||||
// Put returns a []byte to pool for future use,
|
||||
|
@ -56,10 +106,48 @@ func (alloc *defaultAllocator) Put(buf []byte) error {
|
|||
if cap(buf) == 0 || cap(buf) > 65536 || cap(buf) != 1<<bits {
|
||||
return errors.New("allocator Put() incorrect buffer size")
|
||||
}
|
||||
buf = buf[:cap(buf)]
|
||||
|
||||
//nolint
|
||||
//lint:ignore SA6002 ignore temporarily
|
||||
alloc.buffers[bits].Put(buf)
|
||||
switch bits {
|
||||
case 0:
|
||||
alloc.buffers[bits].Put((*[1]byte)(buf))
|
||||
case 1:
|
||||
alloc.buffers[bits].Put((*[1 << 1]byte)(buf))
|
||||
case 2:
|
||||
alloc.buffers[bits].Put((*[1 << 2]byte)(buf))
|
||||
case 3:
|
||||
alloc.buffers[bits].Put((*[1 << 3]byte)(buf))
|
||||
case 4:
|
||||
alloc.buffers[bits].Put((*[1 << 4]byte)(buf))
|
||||
case 5:
|
||||
alloc.buffers[bits].Put((*[1 << 5]byte)(buf))
|
||||
case 6:
|
||||
alloc.buffers[bits].Put((*[1 << 6]byte)(buf))
|
||||
case 7:
|
||||
alloc.buffers[bits].Put((*[1 << 7]byte)(buf))
|
||||
case 8:
|
||||
alloc.buffers[bits].Put((*[1 << 8]byte)(buf))
|
||||
case 9:
|
||||
alloc.buffers[bits].Put((*[1 << 9]byte)(buf))
|
||||
case 10:
|
||||
alloc.buffers[bits].Put((*[1 << 10]byte)(buf))
|
||||
case 11:
|
||||
alloc.buffers[bits].Put((*[1 << 11]byte)(buf))
|
||||
case 12:
|
||||
alloc.buffers[bits].Put((*[1 << 12]byte)(buf))
|
||||
case 13:
|
||||
alloc.buffers[bits].Put((*[1 << 13]byte)(buf))
|
||||
case 14:
|
||||
alloc.buffers[bits].Put((*[1 << 14]byte)(buf))
|
||||
case 15:
|
||||
alloc.buffers[bits].Put((*[1 << 15]byte)(buf))
|
||||
case 16:
|
||||
alloc.buffers[bits].Put((*[1 << 16]byte)(buf))
|
||||
default:
|
||||
panic("invalid pool index")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue