forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/valyala/bytebufferpool/pool.go

238 lines
3.0 KiB
Go
Raw Normal View History

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