Allocate from the most frequently used pool

This commit is contained in:
Aliaksandr Valialkin
2016-06-23 19:08:27 +03:00
parent ee98c9419f
commit 5f9190a306
2 changed files with 65 additions and 14 deletions
+58 -14
View File
@@ -1,6 +1,10 @@
package bytebufferpool
import "sync"
import (
"sort"
"sync"
"sync/atomic"
)
const (
minBitSize = 8
@@ -8,9 +12,15 @@ const (
minSize = 1 << minBitSize
maxSize = 1 << (minBitSize + steps - 1)
calibrateCallsThreshold = 42000
)
type byteBufferPool struct {
idxs [steps]uint64
calls [steps]uint64
calibrating uint64
// Pools are segemented into power-of-two sized buffers
// from minSize bytes to maxSize.
//
@@ -19,9 +29,8 @@ type byteBufferPool struct {
}
func (p *byteBufferPool) Acquire() *ByteBuffer {
pools := &p.pools
for i := 0; i < steps; i++ {
v := pools[i].Get()
for _, idx := range p.idxs {
v := p.pools[idx].Get()
if v != nil {
return v.(*ByteBuffer)
}
@@ -39,19 +48,54 @@ func (p *byteBufferPool) Release(b *ByteBuffer) {
// Drop it.
return
}
bLen := len(b.B)
if bLen > 0 && (bCap>>2) > bLen {
// Under-used buffer capacity.
// Drop it.
//
// Special case: do not drop zero-length buffers -
// this may be the result of Reset call.
idx := bitSize(bCap-1) >> minBitSize
b.B = b.B[:0]
p.pools[idx].Put(b)
if atomic.AddUint64(&p.calls[idx], 1) > calibrateCallsThreshold {
p.calibrate()
}
}
func (p *byteBufferPool) calibrate() {
if !atomic.CompareAndSwapUint64(&p.calibrating, 0, 1) {
return
}
b.B = b.B[:0]
idx := bitSize(bCap-1) >> minBitSize
p.pools[idx].Put(b)
var a callidxs
for i := uint64(0); i < steps; i++ {
a = append(a, callidx{
calls: atomic.SwapUint64(&p.calls[i], 0),
idx: i,
})
}
sort.Sort(a)
for i := 0; i < steps; i++ {
atomic.StoreUint64(&p.idxs[i], a[i].idx)
}
atomic.StoreUint64(&p.calibrating, 0)
}
type callidx struct {
calls uint64
idx uint64
}
type callidxs []callidx
func (ci callidxs) Len() int {
return len(ci)
}
func (ci callidxs) Less(i, j int) bool {
return ci[i].calls > ci[j].calls
}
func (ci callidxs) Swap(i, j int) {
ci[i], ci[j] = ci[j], ci[i]
}
func bitSize(n int) int {
+7
View File
@@ -1,10 +1,17 @@
package bytebufferpool
import (
"math/rand"
"testing"
"time"
)
func TestPoolCalibrate(t *testing.T) {
for i := 0; i < steps*calibrateCallsThreshold; i++ {
testAcquireRelease(t, rand.Intn(10000))
}
}
func TestPoolVariousSizesSerial(t *testing.T) {
testPoolVariousSizes(t)
}