package bufferpool: 1. lock split to bucket, 100x origin, 5x parallel. 2. interface.go

pull/2/head
q191201771 5 years ago
parent 9771b80254
commit 92e9e9e474

4
.gitignore vendored

@ -1,7 +1,9 @@
coverage.txt
coverage.html
profile.out*
tmp
profile.out*
profile*.pdf
*.test
/.idea
/.trash

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package bufferpool
import (
@ -6,72 +14,61 @@ import (
"sync/atomic"
)
var minSize = 1024
var (
minSize = 1024
maxSize = 1073741824
)
type BufferPool struct {
getCount uint32
putCount uint32
hitCount uint32
type bufferPool struct {
getCount uint32
putCount uint32
hitCount uint32
mallocCount uint32
m sync.Mutex
// TODO chef: 这个map可以预申请做成fixed size的
sizeToList map[int]*[]*bytes.Buffer
capToFreeBucket map[int]*item
}
func NewBufferPool() *BufferPool {
return &BufferPool{
sizeToList: make(map[int]*[]*bytes.Buffer),
}
type item struct {
m sync.Mutex
core []*bytes.Buffer
}
func (bp *BufferPool) Get(size int) *bytes.Buffer {
func (bp *bufferPool) Get(size int) *bytes.Buffer {
atomic.AddUint32(&bp.getCount, 1)
ss := up2power(size)
if ss < minSize {
ss = minSize
}
bp.m.Lock()
l, ok := bp.sizeToList[ss]
if !ok {
bp.m.Unlock()
bucket := bp.capToFreeBucket[ss]
bucket.m.Lock()
if len(bucket.core) == 0 {
bucket.m.Unlock()
return bp.newBuffer(ss)
} else {
if len(*l) == 0 {
bp.m.Unlock()
return bp.newBuffer(ss)
}
buf := (*l)[len(*l)-1]
*l = (*l)[:len(*l)-1]
bp.m.Unlock()
buf := bucket.core[len(bucket.core)-1]
bucket.core = bucket.core[:len(bucket.core)-1]
bucket.m.Unlock()
buf.Reset()
atomic.AddUint32(&bp.hitCount, 1)
return buf
}
}
func (bp *BufferPool) Put(buf *bytes.Buffer) {
func (bp *bufferPool) Put(buf *bytes.Buffer) {
atomic.AddUint32(&bp.putCount, 1)
size := down2power(buf.Cap())
if size < minSize {
size = minSize
}
bp.m.Lock()
l, ok := bp.sizeToList[size]
if !ok {
l = new([]*bytes.Buffer)
*l = append(*l, buf)
// TODO
bp.sizeToList[size] = l
} else {
*l = append(*l, buf)
}
bp.m.Unlock()
bucket := bp.capToFreeBucket[size]
bucket.m.Lock()
bucket.core = append(bucket.core, buf)
bucket.m.Unlock()
}
func (bp *BufferPool) newBuffer(n int) *bytes.Buffer {
func (bp *bufferPool) newBuffer(n int) *bytes.Buffer {
var buf bytes.Buffer
buf.Grow(n)
atomic.AddUint32(&bp.mallocCount, 1)
@ -80,7 +77,7 @@ func (bp *BufferPool) newBuffer(n int) *bytes.Buffer {
// @return 范围为 [2, 4, 8, 16, ..., 1073741824]如果大于等于1073741824则直接返回n
func up2power(n int) int {
if n >= 1073741824 {
if n >= maxSize {
return n
}
@ -94,8 +91,8 @@ func up2power(n int) int {
func down2power(n int) int {
if n < 2 {
return 2
} else if n >= 1073741824 {
return 1073741824
} else if n >= maxSize {
return maxSize
}
var i uint32

@ -0,0 +1,93 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package bufferpool
import (
"bytes"
"math/rand"
"testing"
"time"
)
var bp BufferPool
var count int
func size() int {
//return 1024
//ss := []int{1000, 2000, 5000}
////ss := []int{128, 1024, 4096, 16384}
//count++
//return ss[count % 3]
return random(0, 128*1024)
//count++
//if count > 128 * 1024 {
// count = 1
//}
//return count
}
func random(l, r int) int {
return l + (rand.Int() % (r - l))
}
func originFunc() {
var buf bytes.Buffer
size := size()
buf.Grow(size)
}
func bufferPoolFunc() {
size := size()
buf := bp.Get(size)
buf.Grow(size)
bp.Put(buf)
}
func BenchmarkOrigin(b *testing.B) {
for i := 0; i < b.N; i++ {
originFunc()
}
}
func BenchmarkBufferPool(b *testing.B) {
bp = NewBufferPool()
b.ResetTimer()
for i := 0; i < b.N; i++ {
bufferPoolFunc()
}
}
func BenchmarkOriginParallel(b *testing.B) {
for i := 0; i < b.N; i++ {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
originFunc()
}
})
}
}
func BenchmarkBufferPoolParallel(b *testing.B) {
bp = NewBufferPool()
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
bufferPoolFunc()
}
})
}
}
func init() {
rand.Seed(time.Now().Unix())
}

@ -1,91 +1,31 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package bufferpool
import (
"bytes"
"github.com/q191201771/naza/pkg/assert"
"math/rand"
"testing"
"time"
)
var bp *BufferPool
var count int
func TestBufferPool(t *testing.T) {
bp := NewBufferPool()
buf := &bytes.Buffer{}
bp.Get(128)
bp.Put(buf)
buf = bp.Get(4096)
buf = bp.Get(128)
buf.Grow(4096)
bp.Put(buf)
buf = bp.Get(4096)
bp.Put(buf)
}
func size() int {
//return 1024
//ss := []int{1000, 2000, 5000}
////ss := []int{128, 1024, 4096, 16384}
//count++
//return ss[count % 3]
return random(0, 128 * 1024)
}
func random(l, r int) int {
return l + (rand.Int() % (r - l))
}
func origin() {
var buf bytes.Buffer
size := size()
buf.Grow(size)
}
func bufferPool() {
size := size()
buf := bp.Get(size)
buf.Grow(size)
bp.Put(buf)
}
func BenchmarkOrigin(b *testing.B) {
for i := 0; i < b.N; i++ {
origin()
}
}
func BenchmarkBufferPool(b *testing.B) {
bp = NewBufferPool()
b.ResetTimer()
for i := 0; i < b.N; i++ {
bufferPool()
}
}
func BenchmarkOriginParallel(b *testing.B) {
for i := 0; i < b.N; i++ {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
origin()
}
})
}
}
func BenchmarkBufferPoolParallel(b *testing.B) {
bp = NewBufferPool()
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
bufferPool()
}
})
}
}
func TestUp2power(t *testing.T) {
assert.Equal(t, 2, up2power(0))
assert.Equal(t, 2, up2power(1))
@ -121,13 +61,9 @@ func TestDown2power(t *testing.T) {
assert.Equal(t, 512, down2power(1023))
assert.Equal(t, 1024, down2power(1024))
assert.Equal(t, 1024, down2power(1025))
assert.Equal(t, 1073741824 >> 1, down2power(1073741824-1))
assert.Equal(t, 1073741824>>1, down2power(1073741824-1))
assert.Equal(t, 1073741824, down2power(1073741824))
assert.Equal(t, 1073741824, down2power(1073741824+1))
assert.Equal(t, 1073741824, down2power(2047483647-1))
assert.Equal(t, 1073741824, down2power(2047483647))
}
func init() {
rand.Seed(time.Now().Unix())
}

@ -0,0 +1,27 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package bufferpool
import "bytes"
type BufferPool interface {
Get(size int) *bytes.Buffer
Put(buf *bytes.Buffer)
}
func NewBufferPool() BufferPool {
capToFreeBucket := make(map[int]*item)
for i := minSize; i <= maxSize; i <<= 1 {
capToFreeBucket[i] = new(item)
}
return &bufferPool{
capToFreeBucket: capToFreeBucket,
}
}
Loading…
Cancel
Save