mirror of
https://github.com/v2fly/v2ray-core.git
synced 2025-01-02 15:36:41 -05:00
refine buffer allocation
This commit is contained in:
parent
5eac607087
commit
f97e6fa3d2
@ -55,7 +55,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
if size <= buf.Size {
|
if size <= buf.Size {
|
||||||
b = buf.New()
|
b = buf.New()
|
||||||
} else {
|
} else {
|
||||||
b = buf.NewLocal(int(size))
|
b = buf.NewLocal(uint32(size))
|
||||||
}
|
}
|
||||||
if err := b.AppendSupplier(buf.ReadFullFrom(r.reader, int(size))); err != nil {
|
if err := b.AppendSupplier(buf.ReadFullFrom(r.reader, int(size))); err != nil {
|
||||||
b.Release()
|
b.Release()
|
||||||
|
@ -12,8 +12,7 @@ type Supplier func([]byte) (int, error)
|
|||||||
// the buffer into an internal buffer pool, in order to recreate a buffer more
|
// the buffer into an internal buffer pool, in order to recreate a buffer more
|
||||||
// quickly.
|
// quickly.
|
||||||
type Buffer struct {
|
type Buffer struct {
|
||||||
v []byte
|
v []byte
|
||||||
pool *Pool
|
|
||||||
|
|
||||||
start int32
|
start int32
|
||||||
end int32
|
end int32
|
||||||
@ -24,11 +23,8 @@ func (b *Buffer) Release() {
|
|||||||
if b == nil || b.v == nil {
|
if b == nil || b.v == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b.pool != nil {
|
FreeBytes(b.v)
|
||||||
b.pool.Free(b)
|
|
||||||
}
|
|
||||||
b.v = nil
|
b.v = nil
|
||||||
b.pool = nil
|
|
||||||
b.start = 0
|
b.start = 0
|
||||||
b.end = 0
|
b.end = 0
|
||||||
}
|
}
|
||||||
@ -178,13 +174,48 @@ func (b *Buffer) String() string {
|
|||||||
|
|
||||||
// New creates a Buffer with 0 length and 8K capacity.
|
// New creates a Buffer with 0 length and 8K capacity.
|
||||||
func New() *Buffer {
|
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.
|
// 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{
|
return &Buffer{
|
||||||
v: make([]byte, size),
|
v: NewBytes(size),
|
||||||
pool: nil,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,39 +4,29 @@ import (
|
|||||||
"sync"
|
"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 (
|
const (
|
||||||
// Size of a regular buffer.
|
// Size of a regular buffer.
|
||||||
Size = 2 * 1024
|
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),
|
||||||
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package buf_test
|
package buf_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "v2ray.com/core/common/buf"
|
. "v2ray.com/core/common/buf"
|
||||||
@ -42,32 +41,6 @@ func TestBufferString(t *testing.T) {
|
|||||||
assert(buffer.String(), Equals, "Test String")
|
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) {
|
func BenchmarkNewBuffer(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
buffer := New()
|
buffer := New()
|
||||||
|
@ -21,12 +21,13 @@ func NewBytesToBufferReader(reader io.Reader) Reader {
|
|||||||
|
|
||||||
const mediumSize = 8 * 1024
|
const mediumSize = 8 * 1024
|
||||||
const largeSize = 64 * 1024
|
const largeSize = 64 * 1024
|
||||||
|
const xlSize = 128 * 1024
|
||||||
|
|
||||||
func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) {
|
func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) {
|
||||||
b := New()
|
b := New()
|
||||||
err := b.Reset(ReadFrom(r.Reader))
|
err := b.Reset(ReadFrom(r.Reader))
|
||||||
if b.IsFull() {
|
if b.IsFull() {
|
||||||
r.buffer = make([]byte, mediumSize)
|
r.buffer = NewBytes(mediumSize)
|
||||||
}
|
}
|
||||||
if !b.IsEmpty() {
|
if !b.IsEmpty() {
|
||||||
return NewMultiBufferValue(b), nil
|
return NewMultiBufferValue(b), nil
|
||||||
@ -45,11 +46,14 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) {
|
|||||||
if nBytes > 0 {
|
if nBytes > 0 {
|
||||||
mb := NewMultiBufferCap(nBytes/Size + 1)
|
mb := NewMultiBufferCap(nBytes/Size + 1)
|
||||||
mb.Write(r.buffer[:nBytes])
|
mb.Write(r.buffer[:nBytes])
|
||||||
if nBytes == len(r.buffer) && len(r.buffer) == mediumSize {
|
if nBytes == len(r.buffer) && nBytes < xlSize {
|
||||||
r.buffer = make([]byte, largeSize)
|
FreeBytes(r.buffer)
|
||||||
|
r.buffer = NewBytes(uint32(nBytes) + 1)
|
||||||
}
|
}
|
||||||
return mb, nil
|
return mb, nil
|
||||||
}
|
}
|
||||||
|
FreeBytes(r.buffer)
|
||||||
|
r.buffer = nil
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
if size <= buf.Size {
|
if size <= buf.Size {
|
||||||
b = buf.New()
|
b = buf.New()
|
||||||
} else {
|
} else {
|
||||||
b = buf.NewLocal(size)
|
b = buf.NewLocal(uint32(size))
|
||||||
}
|
}
|
||||||
if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil {
|
if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil {
|
||||||
b.Release()
|
b.Release()
|
||||||
|
@ -81,7 +81,7 @@ func (v *ChunkReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
if length > buf.Size {
|
if length > buf.Size {
|
||||||
// Theoretically the size of a chunk is 64K, but most Shadowsocks implementations used <4K buffer.
|
// Theoretically the size of a chunk is 64K, but most Shadowsocks implementations used <4K buffer.
|
||||||
buffer.Release()
|
buffer.Release()
|
||||||
buffer = buf.NewLocal(int(length) + 128)
|
buffer = buf.NewLocal(uint32(length) + 128)
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.Clear()
|
buffer.Clear()
|
||||||
|
Loading…
Reference in New Issue
Block a user