使用chan resource
或者sync.Pool
做对象池在多线程的情况下有可能不够用。
通过预分配可以减少这种损耗,这是以时间换空间:
package bytespool
import "sync"
const (
maxCacheP = 100
maxCacheP2 = 1000
)
// MultiThreadBytesPool provides the source of bytes
type MultiThreadBytesPool struct {
*sync.Pool
maxBytesSize int
cacheP [][]byte
csp int
mutex sync.Mutex
}
// NewMultiThreadBytesPool return a pool reuse bytes between gc
func NewMultiThreadBytesPool(maxBytesSize int) *MultiThreadBytesPool {
mbp := &MultiThreadBytesPool{
Pool: &sync.Pool{
New: MakeNewBytesFunc(maxBytesSize),
},
cacheP: make([][]byte, maxCacheP),
csp: maxCacheP,
maxBytesSize: maxBytesSize,
}
for i := 0; i < maxCacheP; i++ {
mbp.cacheP[i] = make([]byte, maxBytesSize)
}
return mbp
}
// Put bytes into pool
func (bp *MultiThreadBytesPool) Put(b []byte) {
if bp.maxBytesSize <= len(b) {
if bp.csp < maxCacheP {
bp.mutex.Lock()
if bp.csp < maxCacheP {
bp.cacheP[bp.csp] = b
bp.csp++
bp.mutex.Unlock()
} else {
bp.mutex.Unlock()
bp.Pool.Put(b)
}
} else {
bp.Pool.Put(b)
}
} // else ignore this bytes
}
// Get bytes from pool
func (bp *MultiThreadBytesPool) Get() []byte {
if bp.csp != 0 {
bp.mutex.Lock()
if bp.csp != 0 {
bp.csp--
b := bp.cacheP[bp.csp]
bp.mutex.Unlock()
return b
} else {
bp.mutex.Unlock()
return bp.Pool.Get().([]byte)
}
} else {
return bp.Pool.Get().([]byte)
}
}
func BenchmarkGetSet(b *testing.B) {
b.StopTimer()
runtime.GC()
var bp = NewBytesPool(20)
b.StartTimer()
for i := 0; i < b.N; i++ {
s := bp.Get()
bp.Put(s)
}
}
func BenchmarkGetGetSetSet(b *testing.B) {
b.StopTimer()
runtime.GC()
var bp = NewBytesPool(20)
const testN = 350
var s [testN][]byte
b.StartTimer()
for i := 0; i < b.N; i++ {
for j := 0; j < testN; j++ {
s[j] = bp.Get()
}
for j := 0; j < testN; j++ {
bp.Put(s[j])
}
}
}
func BenchmarkGSet(b *testing.B) {
b.StopTimer()
runtime.GC()
var bp = NewBytesPool(20)
x := make(chan bool, 30000)
b.StartTimer()
for i := 0; i < b.N; i++ {
go func() {
s := bp.Get()
bp.Put(s)
x <- true
}()
}
for i := 0; i < b.N; i++ {
<-x
}
}
测试结果:
=== RUN TestBytesPool
--- PASS: TestBytesPool (3.49s)
=== RUN TestBBytesPool
--- PASS: TestBBytesPool (0.35s)
=== RUN TestBBBytesPool
--- PASS: TestBBBytesPool (0.35s)
goos: windows
goarch: amd64
pkg: github.com/Myriad-Dreamin/object-pool/bytes-pool
BenchmarkGetSet-12 20000000 74.1 ns/op 32 B/op 1 allocs/op
BenchmarkMGetSet-12 50000000 37.1 ns/op 0 B/op 0 allocs/op
BenchmarkMGetSet2-12 50000000 36.3 ns/op 0 B/op 0 allocs/op
BenchmarkGetGetSetSet-12 50000 36457 ns/op 11203 B/op 350 allocs/op
BenchmarkMGetGetSetSet-12 50000 30004 ns/op 8001 B/op 250 allocs/op
BenchmarkMGetGetSetSet2-12 100000 13741 ns/op 0 B/op 0 allocs/op
BenchmarkGSet-12 1000000 2628 ns/op 258 B/op 2 allocs/op
BenchmarkMGSet-12 1000000 1358 ns/op 93 B/op 0 allocs/op
BenchmarkMGSet2-12 1000000 1114 ns/op 93 B/op 0 allocs/op
PASS
cacheP设置为常量是比较大的一个优化,在实际情况下多设置几个P,避免空间分配过多。