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,避免空间分配过多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值