使用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,避免空間分配過多。