| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 | package bytebufferpoolimport (	"sort"	"sync"	"sync/atomic")const (	minBitSize = 6 // 2**6=64 is a CPU cache line size	steps      = 20	minSize = 1 << minBitSize	maxSize = 1 << (minBitSize + steps - 1)	calibrateCallsThreshold = 42000	maxPercentile           = 0.95)// Pool represents byte buffer pool.//// Distinct pools may be used for distinct types of byte buffers.// Properly determined byte buffer types with their own pools may help reducing// memory waste.type Pool struct {	calls       [steps]uint64	calibrating uint64	defaultSize uint64	maxSize     uint64	pool sync.Pool}var defaultPool Pool// Get returns an empty byte buffer from the pool.//// Got byte buffer may be returned to the pool via Put call.// This reduces the number of memory allocations required for byte buffer// management.func Get() *ByteBuffer { return defaultPool.Get() }// Get returns new byte buffer with zero length.//// The byte buffer may be returned to the pool via Put after the use// in order to minimize GC overhead.func (p *Pool) Get() *ByteBuffer {	v := p.pool.Get()	if v != nil {		return v.(*ByteBuffer)	}	return &ByteBuffer{		B: make([]byte, 0, atomic.LoadUint64(&p.defaultSize)),	}}// Put returns byte buffer to the pool.//// ByteBuffer.B mustn't be touched after returning it to the pool.// Otherwise data races will occur.func Put(b *ByteBuffer) { defaultPool.Put(b) }// Put releases byte buffer obtained via Get to the pool.//// The buffer mustn't be accessed after returning to the pool.func (p *Pool) Put(b *ByteBuffer) {	idx := index(len(b.B))	if atomic.AddUint64(&p.calls[idx], 1) > calibrateCallsThreshold {		p.calibrate()	}	maxSize := int(atomic.LoadUint64(&p.maxSize))	if maxSize == 0 || cap(b.B) <= maxSize {		b.Reset()		p.pool.Put(b)	}}func (p *Pool) calibrate() {	if !atomic.CompareAndSwapUint64(&p.calibrating, 0, 1) {		return	}	a := make(callSizes, 0, steps)	var callsSum uint64	for i := uint64(0); i < steps; i++ {		calls := atomic.SwapUint64(&p.calls[i], 0)		callsSum += calls		a = append(a, callSize{			calls: calls,			size:  minSize << i,		})	}	sort.Sort(a)	defaultSize := a[0].size	maxSize := defaultSize	maxSum := uint64(float64(callsSum) * maxPercentile)	callsSum = 0	for i := 0; i < steps; i++ {		if callsSum > maxSum {			break		}		callsSum += a[i].calls		size := a[i].size		if size > maxSize {			maxSize = size		}	}	atomic.StoreUint64(&p.defaultSize, defaultSize)	atomic.StoreUint64(&p.maxSize, maxSize)	atomic.StoreUint64(&p.calibrating, 0)}type callSize struct {	calls uint64	size  uint64}type callSizes []callSizefunc (ci callSizes) Len() int {	return len(ci)}func (ci callSizes) Less(i, j int) bool {	return ci[i].calls > ci[j].calls}func (ci callSizes) Swap(i, j int) {	ci[i], ci[j] = ci[j], ci[i]}func index(n int) int {	n--	n >>= minBitSize	idx := 0	for n > 0 {		n >>= 1		idx++	}	if idx >= steps {		idx = steps - 1	}	return idx}
 |