Files
bytebufferpool/pool.go
T
Aliaksandr Valialkin a1c2e6cf76 Revert "Return back pools segementation by size".
This reverts commit 08eb8e13d0,
since it led to high memory usage on the production box.

The commit need additional investigation.
2016-06-23 22:19:16 +03:00

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
}