mirror of
https://github.com/valyala/bytebufferpool.git
synced 2026-06-14 13:26:35 +03:00
a1c2e6cf76
This reverts commit 08eb8e13d0,
since it led to high memory usage on the production box.
The commit need additional investigation.
126 lines
2.0 KiB
Go
126 lines
2.0 KiB
Go
package bytebufferpool
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
const (
|
|
minBitSize = 6
|
|
steps = 20
|
|
|
|
minSize = 1 << minBitSize
|
|
maxSize = 1 << (minBitSize + steps - 1)
|
|
|
|
calibrateCallsThreshold = 42000
|
|
maxPercentile = 0.95
|
|
)
|
|
|
|
type byteBufferPool struct {
|
|
calls [steps]uint64
|
|
calibrating uint64
|
|
|
|
defaultSize uint64
|
|
maxSize uint64
|
|
|
|
pool sync.Pool
|
|
}
|
|
|
|
func (p *byteBufferPool) Acquire() *ByteBuffer {
|
|
v := p.pool.Get()
|
|
if v != nil {
|
|
return v.(*ByteBuffer)
|
|
}
|
|
return &ByteBuffer{
|
|
B: make([]byte, 0, atomic.LoadUint64(&p.defaultSize)),
|
|
}
|
|
}
|
|
|
|
func (p *byteBufferPool) Release(b *ByteBuffer) {
|
|
bSize := len(b.B)
|
|
idx := bitSize(bSize-1) - minBitSize
|
|
if idx < 0 {
|
|
idx = 0
|
|
} else if idx >= steps {
|
|
idx = steps - 1
|
|
}
|
|
|
|
if atomic.AddUint64(&p.calls[idx], 1) > calibrateCallsThreshold {
|
|
p.calibrate()
|
|
}
|
|
|
|
maxSize := int(atomic.LoadUint64(&p.maxSize))
|
|
if maxSize > 0 && bSize <= maxSize {
|
|
b.B = b.B[:0]
|
|
p.pool.Put(b)
|
|
}
|
|
}
|
|
|
|
func (p *byteBufferPool) 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 []callSize
|
|
|
|
func (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 bitSize(n int) int {
|
|
s := 0
|
|
for n > 0 {
|
|
n >>= 1
|
|
s++
|
|
}
|
|
return s
|
|
}
|