forked from toolshed/abra
		
	
		
			
				
	
	
		
			195 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
|  *
 | |
|  * Copyright 2024 gRPC authors.
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| package mem
 | |
| 
 | |
| import (
 | |
| 	"sort"
 | |
| 	"sync"
 | |
| 
 | |
| 	"google.golang.org/grpc/internal"
 | |
| )
 | |
| 
 | |
| // BufferPool is a pool of buffers that can be shared and reused, resulting in
 | |
| // decreased memory allocation.
 | |
| type BufferPool interface {
 | |
| 	// Get returns a buffer with specified length from the pool.
 | |
| 	Get(length int) *[]byte
 | |
| 
 | |
| 	// Put returns a buffer to the pool.
 | |
| 	Put(*[]byte)
 | |
| }
 | |
| 
 | |
| var defaultBufferPoolSizes = []int{
 | |
| 	256,
 | |
| 	4 << 10,  // 4KB (go page size)
 | |
| 	16 << 10, // 16KB (max HTTP/2 frame size used by gRPC)
 | |
| 	32 << 10, // 32KB (default buffer size for io.Copy)
 | |
| 	1 << 20,  // 1MB
 | |
| }
 | |
| 
 | |
| var defaultBufferPool BufferPool
 | |
| 
 | |
| func init() {
 | |
| 	defaultBufferPool = NewTieredBufferPool(defaultBufferPoolSizes...)
 | |
| 
 | |
| 	internal.SetDefaultBufferPoolForTesting = func(pool BufferPool) {
 | |
| 		defaultBufferPool = pool
 | |
| 	}
 | |
| 
 | |
| 	internal.SetBufferPoolingThresholdForTesting = func(threshold int) {
 | |
| 		bufferPoolingThreshold = threshold
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // DefaultBufferPool returns the current default buffer pool. It is a BufferPool
 | |
| // created with NewBufferPool that uses a set of default sizes optimized for
 | |
| // expected workflows.
 | |
| func DefaultBufferPool() BufferPool {
 | |
| 	return defaultBufferPool
 | |
| }
 | |
| 
 | |
| // NewTieredBufferPool returns a BufferPool implementation that uses multiple
 | |
| // underlying pools of the given pool sizes.
 | |
| func NewTieredBufferPool(poolSizes ...int) BufferPool {
 | |
| 	sort.Ints(poolSizes)
 | |
| 	pools := make([]*sizedBufferPool, len(poolSizes))
 | |
| 	for i, s := range poolSizes {
 | |
| 		pools[i] = newSizedBufferPool(s)
 | |
| 	}
 | |
| 	return &tieredBufferPool{
 | |
| 		sizedPools: pools,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // tieredBufferPool implements the BufferPool interface with multiple tiers of
 | |
| // buffer pools for different sizes of buffers.
 | |
| type tieredBufferPool struct {
 | |
| 	sizedPools   []*sizedBufferPool
 | |
| 	fallbackPool simpleBufferPool
 | |
| }
 | |
| 
 | |
| func (p *tieredBufferPool) Get(size int) *[]byte {
 | |
| 	return p.getPool(size).Get(size)
 | |
| }
 | |
| 
 | |
| func (p *tieredBufferPool) Put(buf *[]byte) {
 | |
| 	p.getPool(cap(*buf)).Put(buf)
 | |
| }
 | |
| 
 | |
| func (p *tieredBufferPool) getPool(size int) BufferPool {
 | |
| 	poolIdx := sort.Search(len(p.sizedPools), func(i int) bool {
 | |
| 		return p.sizedPools[i].defaultSize >= size
 | |
| 	})
 | |
| 
 | |
| 	if poolIdx == len(p.sizedPools) {
 | |
| 		return &p.fallbackPool
 | |
| 	}
 | |
| 
 | |
| 	return p.sizedPools[poolIdx]
 | |
| }
 | |
| 
 | |
| // sizedBufferPool is a BufferPool implementation that is optimized for specific
 | |
| // buffer sizes. For example, HTTP/2 frames within gRPC have a default max size
 | |
| // of 16kb and a sizedBufferPool can be configured to only return buffers with a
 | |
| // capacity of 16kb. Note that however it does not support returning larger
 | |
| // buffers and in fact panics if such a buffer is requested. Because of this,
 | |
| // this BufferPool implementation is not meant to be used on its own and rather
 | |
| // is intended to be embedded in a tieredBufferPool such that Get is only
 | |
| // invoked when the required size is smaller than or equal to defaultSize.
 | |
| type sizedBufferPool struct {
 | |
| 	pool        sync.Pool
 | |
| 	defaultSize int
 | |
| }
 | |
| 
 | |
| func (p *sizedBufferPool) Get(size int) *[]byte {
 | |
| 	buf := p.pool.Get().(*[]byte)
 | |
| 	b := *buf
 | |
| 	clear(b[:cap(b)])
 | |
| 	*buf = b[:size]
 | |
| 	return buf
 | |
| }
 | |
| 
 | |
| func (p *sizedBufferPool) Put(buf *[]byte) {
 | |
| 	if cap(*buf) < p.defaultSize {
 | |
| 		// Ignore buffers that are too small to fit in the pool. Otherwise, when
 | |
| 		// Get is called it will panic as it tries to index outside the bounds
 | |
| 		// of the buffer.
 | |
| 		return
 | |
| 	}
 | |
| 	p.pool.Put(buf)
 | |
| }
 | |
| 
 | |
| func newSizedBufferPool(size int) *sizedBufferPool {
 | |
| 	return &sizedBufferPool{
 | |
| 		pool: sync.Pool{
 | |
| 			New: func() any {
 | |
| 				buf := make([]byte, size)
 | |
| 				return &buf
 | |
| 			},
 | |
| 		},
 | |
| 		defaultSize: size,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var _ BufferPool = (*simpleBufferPool)(nil)
 | |
| 
 | |
| // simpleBufferPool is an implementation of the BufferPool interface that
 | |
| // attempts to pool buffers with a sync.Pool. When Get is invoked, it tries to
 | |
| // acquire a buffer from the pool but if that buffer is too small, it returns it
 | |
| // to the pool and creates a new one.
 | |
| type simpleBufferPool struct {
 | |
| 	pool sync.Pool
 | |
| }
 | |
| 
 | |
| func (p *simpleBufferPool) Get(size int) *[]byte {
 | |
| 	bs, ok := p.pool.Get().(*[]byte)
 | |
| 	if ok && cap(*bs) >= size {
 | |
| 		*bs = (*bs)[:size]
 | |
| 		return bs
 | |
| 	}
 | |
| 
 | |
| 	// A buffer was pulled from the pool, but it is too small. Put it back in
 | |
| 	// the pool and create one large enough.
 | |
| 	if ok {
 | |
| 		p.pool.Put(bs)
 | |
| 	}
 | |
| 
 | |
| 	b := make([]byte, size)
 | |
| 	return &b
 | |
| }
 | |
| 
 | |
| func (p *simpleBufferPool) Put(buf *[]byte) {
 | |
| 	p.pool.Put(buf)
 | |
| }
 | |
| 
 | |
| var _ BufferPool = NopBufferPool{}
 | |
| 
 | |
| // NopBufferPool is a buffer pool that returns new buffers without pooling.
 | |
| type NopBufferPool struct{}
 | |
| 
 | |
| // Get returns a buffer with specified length from the pool.
 | |
| func (NopBufferPool) Get(length int) *[]byte {
 | |
| 	b := make([]byte, length)
 | |
| 	return &b
 | |
| }
 | |
| 
 | |
| // Put returns a buffer to the pool.
 | |
| func (NopBufferPool) Put(*[]byte) {
 | |
| }
 |