1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-06-10 09:50:43 +00:00

refine buffer allocation

This commit is contained in:
Darien Raymond 2018-03-11 23:06:04 +01:00
parent 5eac607087
commit f97e6fa3d2
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
7 changed files with 72 additions and 74 deletions

View File

@ -55,7 +55,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
if size <= buf.Size {
b = buf.New()
} else {
b = buf.NewLocal(int(size))
b = buf.NewLocal(uint32(size))
}
if err := b.AppendSupplier(buf.ReadFullFrom(r.reader, int(size))); err != nil {
b.Release()

View File

@ -12,8 +12,7 @@ type Supplier func([]byte) (int, error)
// the buffer into an internal buffer pool, in order to recreate a buffer more
// quickly.
type Buffer struct {
v []byte
pool *Pool
v []byte
start int32
end int32
@ -24,11 +23,8 @@ func (b *Buffer) Release() {
if b == nil || b.v == nil {
return
}
if b.pool != nil {
b.pool.Free(b)
}
FreeBytes(b.v)
b.v = nil
b.pool = nil
b.start = 0
b.end = 0
}
@ -178,13 +174,48 @@ func (b *Buffer) String() string {
// New creates a Buffer with 0 length and 8K capacity.
func New() *Buffer {
return mediumPool.Allocate()
return &Buffer{
v: pool2k.Get().([]byte),
}
}
// NewLocal creates and returns a buffer with 0 length and given capacity on current thread.
func NewLocal(size int) *Buffer {
func NewLocal(size uint32) *Buffer {
return &Buffer{
v: make([]byte, size),
pool: nil,
v: NewBytes(size),
}
}
func NewBytes(size uint32) []byte {
if size > 128*1024 {
return make([]byte, size)
}
if size > 64*1024 {
return pool128k.Get().([]byte)
}
if size > 8*1024 {
return pool64k.Get().([]byte)
}
if size > 2*1024 {
return pool8k.Get().([]byte)
}
return pool2k.Get().([]byte)
}
func FreeBytes(b []byte) {
size := len(b)
switch {
case size >= 128*1024:
pool128k.Put(b)
case size >= 64*1024:
pool64k.Put(b)
case size >= 8*1024:
pool8k.Put(b)
case size >= 2*1024:
pool2k.Put(b)
}
}

View File

@ -4,39 +4,29 @@ import (
"sync"
)
// Pool provides functionality to generate and recycle buffers on demand.
type Pool struct {
allocator *sync.Pool
}
// NewPool creates a SyncPool with given buffer size.
func NewPool(bufferSize uint32) *Pool {
pool := &Pool{
allocator: &sync.Pool{
New: func() interface{} { return make([]byte, bufferSize) },
},
}
return pool
}
// Allocate either returns a unused buffer from the pool, or generates a new one from system.
func (p *Pool) Allocate() *Buffer {
return &Buffer{
v: p.allocator.Get().([]byte),
pool: p,
}
}
// // Free recycles the given buffer.
func (p *Pool) Free(buffer *Buffer) {
if buffer.v != nil {
p.allocator.Put(buffer.v)
}
}
const (
// Size of a regular buffer.
Size = 2 * 1024
)
var mediumPool = NewPool(Size)
func createAllocFunc(size uint32) func() interface{} {
return func() interface{} {
return make([]byte, size)
}
}
var pool2k = &sync.Pool{
New: createAllocFunc(2 * 1024),
}
var pool8k = &sync.Pool{
New: createAllocFunc(8 * 1024),
}
var pool64k = &sync.Pool{
New: createAllocFunc(64 * 1024),
}
var pool128k = &sync.Pool{
New: createAllocFunc(128 * 1024),
}

View File

@ -1,7 +1,6 @@
package buf_test
import (
"crypto/rand"
"testing"
. "v2ray.com/core/common/buf"
@ -42,32 +41,6 @@ func TestBufferString(t *testing.T) {
assert(buffer.String(), Equals, "Test String")
}
func TestBufferWrite(t *testing.T) {
assert := With(t)
buffer := NewLocal(8)
nBytes, err := buffer.Write([]byte("abcd"))
assert(err, IsNil)
assert(nBytes, Equals, 4)
nBytes, err = buffer.Write([]byte("abcde"))
assert(err, IsNil)
assert(nBytes, Equals, 4)
assert(buffer.String(), Equals, "abcdabcd")
}
func TestSyncPool(t *testing.T) {
assert := With(t)
p := NewPool(32)
b := p.Allocate()
assert(b.Len(), Equals, 0)
assert(b.AppendSupplier(ReadFrom(rand.Reader)), IsNil)
assert(b.Len(), Equals, 32)
b.Release()
}
func BenchmarkNewBuffer(b *testing.B) {
for i := 0; i < b.N; i++ {
buffer := New()

View File

@ -21,12 +21,13 @@ func NewBytesToBufferReader(reader io.Reader) Reader {
const mediumSize = 8 * 1024
const largeSize = 64 * 1024
const xlSize = 128 * 1024
func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) {
b := New()
err := b.Reset(ReadFrom(r.Reader))
if b.IsFull() {
r.buffer = make([]byte, mediumSize)
r.buffer = NewBytes(mediumSize)
}
if !b.IsEmpty() {
return NewMultiBufferValue(b), nil
@ -45,11 +46,14 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) {
if nBytes > 0 {
mb := NewMultiBufferCap(nBytes/Size + 1)
mb.Write(r.buffer[:nBytes])
if nBytes == len(r.buffer) && len(r.buffer) == mediumSize {
r.buffer = make([]byte, largeSize)
if nBytes == len(r.buffer) && nBytes < xlSize {
FreeBytes(r.buffer)
r.buffer = NewBytes(uint32(nBytes) + 1)
}
return mb, nil
}
FreeBytes(r.buffer)
r.buffer = nil
return nil, err
}

View File

@ -128,7 +128,7 @@ func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
if size <= buf.Size {
b = buf.New()
} else {
b = buf.NewLocal(size)
b = buf.NewLocal(uint32(size))
}
if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil {
b.Release()

View File

@ -81,7 +81,7 @@ func (v *ChunkReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
if length > buf.Size {
// Theoretically the size of a chunk is 64K, but most Shadowsocks implementations used <4K buffer.
buffer.Release()
buffer = buf.NewLocal(int(length) + 128)
buffer = buf.NewLocal(uint32(length) + 128)
}
buffer.Clear()