golang降低多線程爭用對象池帶來的損耗

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章