From 0b60be94380b5850a3271ccc54880e41805b0e08 Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Sat, 25 Apr 2020 00:30:40 +0800 Subject: [PATCH] Improve: pool buffer alloc --- common/pool/alloc.go | 65 +++++++++++++++++++++++++++++++++++ common/pool/alloc_test.go | 48 ++++++++++++++++++++++++++ common/pool/pool.go | 15 ++++---- component/simple-obfs/http.go | 8 ++--- component/simple-obfs/tls.go | 12 +++---- component/vmess/aead.go | 12 +++---- component/vmess/chunk.go | 16 ++++----- proxy/redir/udp.go | 4 +-- proxy/redir/utils.go | 2 +- proxy/socks/udp.go | 6 ++-- proxy/socks/utils.go | 2 +- tunnel/connection.go | 16 ++++----- 12 files changed, 158 insertions(+), 48 deletions(-) create mode 100644 common/pool/alloc.go create mode 100644 common/pool/alloc_test.go diff --git a/common/pool/alloc.go b/common/pool/alloc.go new file mode 100644 index 00000000..e9a7d52c --- /dev/null +++ b/common/pool/alloc.go @@ -0,0 +1,65 @@ +package pool + +// Inspired by https://github.com/xtaci/smux/blob/master/alloc.go + +import ( + "errors" + "math/bits" + "sync" +) + +var defaultAllocator *Allocator + +func init() { + defaultAllocator = NewAllocator() +} + +// Allocator for incoming frames, optimized to prevent overwriting after zeroing +type Allocator struct { + buffers []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 NewAllocator() *Allocator { + alloc := new(Allocator) + alloc.buffers = make([]sync.Pool, 17) // 1B -> 64K + for k := range alloc.buffers { + i := k + alloc.buffers[k].New = func() interface{} { + return make([]byte, 1< 65536 { + return nil + } + + bits := msb(size) + if size == 1< 65536 || cap(buf) != 1<= realLen { - pool.BufPool.Put(buf[:cap(buf)]) + pool.Put(buf) return n, nil } diff --git a/component/vmess/chunk.go b/component/vmess/chunk.go index 7f30d0f0..cd7ea57b 100644 --- a/component/vmess/chunk.go +++ b/component/vmess/chunk.go @@ -34,7 +34,7 @@ func (cr *chunkReader) Read(b []byte) (int, error) { n := copy(b, cr.buf[cr.offset:]) cr.offset += n if cr.offset == len(cr.buf) { - pool.BufPool.Put(cr.buf[:cap(cr.buf)]) + pool.Put(cr.buf) cr.buf = nil } return n, nil @@ -59,15 +59,15 @@ func (cr *chunkReader) Read(b []byte) (int, error) { return size, nil } - buf := pool.BufPool.Get().([]byte) - _, err = io.ReadFull(cr.Reader, buf[:size]) + buf := pool.Get(size) + _, err = io.ReadFull(cr.Reader, buf) if err != nil { - pool.BufPool.Put(buf[:cap(buf)]) + pool.Put(buf) return 0, err } - n := copy(b, cr.buf[:]) + n := copy(b, buf) cr.offset = n - cr.buf = buf[:size] + cr.buf = buf return n, nil } @@ -76,8 +76,8 @@ type chunkWriter struct { } func (cw *chunkWriter) Write(b []byte) (n int, err error) { - buf := pool.BufPool.Get().([]byte) - defer pool.BufPool.Put(buf[:cap(buf)]) + buf := pool.Get(pool.RelayBufferSize) + defer pool.Put(buf) length := len(b) for { if length == 0 { diff --git a/proxy/redir/udp.go b/proxy/redir/udp.go index 9b342996..bcba6ffd 100644 --- a/proxy/redir/udp.go +++ b/proxy/redir/udp.go @@ -34,10 +34,10 @@ func NewRedirUDPProxy(addr string) (*RedirUDPListener, error) { go func() { oob := make([]byte, 1024) for { - buf := pool.BufPool.Get().([]byte) + buf := pool.Get(pool.RelayBufferSize) n, oobn, _, lAddr, err := c.ReadMsgUDP(buf, oob) if err != nil { - pool.BufPool.Put(buf[:cap(buf)]) + pool.Put(buf) if rl.closed { break } diff --git a/proxy/redir/utils.go b/proxy/redir/utils.go index 5c737889..e78563f4 100644 --- a/proxy/redir/utils.go +++ b/proxy/redir/utils.go @@ -33,6 +33,6 @@ func (c *packet) LocalAddr() net.Addr { } func (c *packet) Drop() { - pool.BufPool.Put(c.buf[:cap(c.buf)]) + pool.Put(c.buf) return } diff --git a/proxy/socks/udp.go b/proxy/socks/udp.go index 9d77139c..90ccad1b 100644 --- a/proxy/socks/udp.go +++ b/proxy/socks/udp.go @@ -31,10 +31,10 @@ func NewSocksUDPProxy(addr string) (*SockUDPListener, error) { sl := &SockUDPListener{l, addr, false} go func() { for { - buf := pool.BufPool.Get().([]byte) + buf := pool.Get(pool.RelayBufferSize) n, remoteAddr, err := l.ReadFrom(buf) if err != nil { - pool.BufPool.Put(buf[:cap(buf)]) + pool.Put(buf) if sl.closed { break } @@ -60,7 +60,7 @@ func handleSocksUDP(pc net.PacketConn, buf []byte, addr net.Addr) { target, payload, err := socks5.DecodeUDPPacket(buf) if err != nil { // Unresolved UDP packet, return buffer to the pool - pool.BufPool.Put(buf[:cap(buf)]) + pool.Put(buf) return } packet := &packet{ diff --git a/proxy/socks/utils.go b/proxy/socks/utils.go index 3ee52f5f..6b77a40f 100644 --- a/proxy/socks/utils.go +++ b/proxy/socks/utils.go @@ -33,6 +33,6 @@ func (c *packet) LocalAddr() net.Addr { } func (c *packet) Drop() { - pool.BufPool.Put(c.bufRef[:cap(c.bufRef)]) + pool.Put(c.bufRef) return } diff --git a/tunnel/connection.go b/tunnel/connection.go index f9fa5038..65d45d46 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -61,9 +61,9 @@ func handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) { } // even if resp.Write write body to the connection, but some http request have to Copy to close it - buf := pool.BufPool.Get().([]byte) + buf := pool.Get(pool.RelayBufferSize) _, err = io.CopyBuffer(request, resp.Body, buf) - pool.BufPool.Put(buf[:cap(buf)]) + pool.Put(buf) if err != nil && err != io.EOF { break } @@ -90,8 +90,8 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata } func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr net.Addr) { - buf := pool.BufPool.Get().([]byte) - defer pool.BufPool.Put(buf[:cap(buf)]) + buf := pool.Get(pool.RelayBufferSize) + defer pool.Put(buf) defer natTable.Delete(key) defer pc.Close() @@ -122,16 +122,16 @@ func relay(leftConn, rightConn net.Conn) { ch := make(chan error) go func() { - buf := pool.BufPool.Get().([]byte) + buf := pool.Get(pool.RelayBufferSize) _, err := io.CopyBuffer(leftConn, rightConn, buf) - pool.BufPool.Put(buf[:cap(buf)]) + pool.Put(buf) leftConn.SetReadDeadline(time.Now()) ch <- err }() - buf := pool.BufPool.Get().([]byte) + buf := pool.Get(pool.RelayBufferSize) io.CopyBuffer(rightConn, leftConn, buf) - pool.BufPool.Put(buf[:cap(buf)]) + pool.Put(buf) rightConn.SetReadDeadline(time.Now()) <-ch }