-
Notifications
You must be signed in to change notification settings - Fork 134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Autotuning low and high bins' limits. #13
base: master
Are you sure you want to change the base?
Changes from all commits
b06cf14
02c4518
a6bad01
3daaec9
4c5299e
885a933
edcb605
8b84991
fb73bd8
e492b2c
fe3bacb
ce18725
b4c6b01
1e3ef50
6f29468
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
module github.com/valyala/bytebufferpool | ||
module github.com/gallir/bytebufferpool | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert this |
||
go 1.12 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,11 +7,11 @@ import ( | |
) | ||
|
||
const ( | ||
minBitSize = 6 // 2**6=64 is a CPU cache line size | ||
steps = 20 | ||
defaultMinBitSize = 6 // 2**6=64 is a CPU cache line size | ||
steps = 20 | ||
|
||
minSize = 1 << minBitSize | ||
maxSize = 1 << (minBitSize + steps - 1) | ||
defaultMinSize = 1 << defaultMinBitSize | ||
defaultMaxSize = 1 << (defaultMinBitSize + steps - 1) | ||
|
||
calibrateCallsThreshold = 42000 | ||
maxPercentile = 0.95 | ||
|
@@ -29,6 +29,9 @@ type Pool struct { | |
defaultSize uint64 | ||
maxSize uint64 | ||
|
||
minBitSize uint64 | ||
minSize uint64 | ||
|
||
pool sync.Pool | ||
} | ||
|
||
|
@@ -48,13 +51,56 @@ func Get() *ByteBuffer { return defaultPool.Get() } | |
func (p *Pool) Get() *ByteBuffer { | ||
v := p.pool.Get() | ||
if v != nil { | ||
return v.(*ByteBuffer) | ||
b := v.(*ByteBuffer) | ||
b.Reset() | ||
return b | ||
} | ||
return &ByteBuffer{ | ||
B: make([]byte, 0, atomic.LoadUint64(&p.defaultSize)), | ||
} | ||
} | ||
|
||
// GetLen returns a buufer with its | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. buffer |
||
// []byte slice of the exact len as specified | ||
// | ||
// The byte buffer may be returned to the pool via Put after the use | ||
// in order to minimize GC overhead. | ||
func GetLen(s int) *ByteBuffer { return defaultPool.GetLen(s) } | ||
|
||
// GetLen return a buufer with its | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. buffer |
||
// []byte slice of the exact len as specified | ||
// | ||
// The byte buffer may be returned to the pool via Put after the use | ||
// in order to minimize GC overhead. | ||
func (p *Pool) GetLen(s int) *ByteBuffer { | ||
v := p.pool.Get() | ||
if v == nil { | ||
size := int(p.minSize << uint(index(p.minBitSize, s))) | ||
if size < s { | ||
size = s | ||
} | ||
return &ByteBuffer{ | ||
B: make([]byte, s, size), | ||
} | ||
} | ||
|
||
b := v.(*ByteBuffer) | ||
if cap(b.B) >= s { | ||
b.B = b.B[:s] | ||
return b | ||
} | ||
|
||
// The size is smaller, return it to the pool and create another one | ||
p.pool.Put(b) | ||
size := int(p.minSize << uint(index(p.minBitSize, s))) | ||
if size < s { | ||
size = s | ||
} | ||
return &ByteBuffer{ | ||
B: make([]byte, s, size), | ||
} | ||
} | ||
|
||
// Put returns byte buffer to the pool. | ||
// | ||
// ByteBuffer.B mustn't be touched after returning it to the pool. | ||
|
@@ -65,15 +111,18 @@ func Put(b *ByteBuffer) { defaultPool.Put(b) } | |
// | ||
// The buffer mustn't be accessed after returning to the pool. | ||
func (p *Pool) Put(b *ByteBuffer) { | ||
idx := index(len(b.B)) | ||
if p.minBitSize == 0 { | ||
p.initBins() | ||
} | ||
|
||
idx := index(p.minBitSize, 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) | ||
} | ||
} | ||
|
@@ -83,16 +132,30 @@ func (p *Pool) calibrate() { | |
return | ||
} | ||
|
||
if p.minBitSize == 0 { | ||
p.initBins() | ||
} | ||
|
||
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, | ||
size: p.minSize << i, | ||
}) | ||
} | ||
if p.minBitSize+steps < 32 && a[steps-1].calls > a[0].calls { | ||
// Increase the first bin's size | ||
p.resizeBins(p.minBitSize + 1) | ||
} else if p.minBitSize > defaultMinBitSize && | ||
a[0].calls > 0 && | ||
a[steps-2].calls == 0 && | ||
a[steps-1].calls == 0 { | ||
// Decrease the size of first bin's size | ||
p.resizeBins(p.minBitSize - 1) | ||
} | ||
sort.Sort(a) | ||
|
||
defaultSize := a[0].size | ||
|
@@ -117,6 +180,16 @@ func (p *Pool) calibrate() { | |
atomic.StoreUint64(&p.calibrating, 0) | ||
} | ||
|
||
func (p *Pool) resizeBins(minBitSize uint64) { | ||
atomic.StoreUint64(&p.minBitSize, minBitSize) | ||
atomic.StoreUint64(&p.minSize, 1<<minBitSize) | ||
} | ||
|
||
func (p *Pool) initBins() { | ||
atomic.StoreUint64(&p.minBitSize, defaultMinBitSize) | ||
atomic.StoreUint64(&p.minSize, 1<<defaultMinBitSize) | ||
} | ||
|
||
type callSize struct { | ||
calls uint64 | ||
size uint64 | ||
|
@@ -136,7 +209,7 @@ func (ci callSizes) Swap(i, j int) { | |
ci[i], ci[j] = ci[j], ci[i] | ||
} | ||
|
||
func index(n int) int { | ||
func index(minBitSize uint64, n int) int { | ||
n-- | ||
n >>= minBitSize | ||
idx := 0 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revert this