Skip to content

Instantly share code, notes, and snippets.

@redbo
Created February 5, 2015 21:08
Show Gist options
  • Save redbo/9791719f609e63215458 to your computer and use it in GitHub Desktop.
Save redbo/9791719f609e63215458 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"runtime"
"sync"
"sync/atomic"
"time"
"unsafe"
)
// ChannelPool
type ChannelPool chan interface{}
func (f ChannelPool) Get() interface{} {
var v interface{}
select {
case v = <-f:
return v
default:
return nil
}
}
func (f ChannelPool) Put(v interface{}) {
select {
case f <- v:
default:
}
}
func NewChannelPool(size int) ChannelPool {
return make(ChannelPool, size)
}
// MutexPool
type MutexPool struct {
sync.Mutex
list []interface{}
top int
}
func (m *MutexPool) Put(v interface{}) {
m.Lock()
if m.top < len(m.list) {
m.list[m.top] = v
m.top++
}
m.Unlock()
}
func (m *MutexPool) Get() interface{} {
var ret interface{} = nil
m.Lock()
if m.top > 0 {
ret = m.list[m.top-1]
m.top--
}
m.Unlock()
return ret
}
func NewMutexPool(size int) *MutexPool {
return &MutexPool{list: make([]interface{}, size)}
}
// AtomicPool
type AtomicPool struct {
list []interface{}
pop []*int32
}
func (a *AtomicPool) Get() interface{} {
for i, p := range a.pop {
if atomic.CompareAndSwapInt32(p, 2, 1) {
ret := a.list[i]
atomic.StoreInt32(p, 0)
return ret
}
}
return nil
}
func (a *AtomicPool) Put(v interface{}) {
for i, p := range a.pop {
if atomic.CompareAndSwapInt32(p, 0, 1) {
a.list[i] = v
atomic.StoreInt32(p, 2)
return
}
}
}
func NewAtomicPool(size int) *AtomicPool {
p := &AtomicPool{list: make([]interface{}, size), pop: make([]*int32, size)}
for i := 0; i < size; i++ {
p.pop[i] = new(int32)
}
return p
}
// AtomicPool2
type AtomicPool2 []*unsafe.Pointer
func (a AtomicPool2) Get() interface{} {
for _, p := range a {
if v := atomic.SwapPointer(p, nil); v != nil {
return *(*interface{})(v)
}
}
return nil
}
func (a AtomicPool2) Put(v interface{}) {
vp := unsafe.Pointer(&v)
for _, p := range a {
if atomic.CompareAndSwapPointer(p, nil, vp) {
return
}
}
}
func NewAtomicPool2(size int) AtomicPool2 {
a := make(AtomicPool2, size)
for i := range a {
a[i] = new(unsafe.Pointer)
}
return a
}
// benchmarking stuff
type pool interface {
Get() interface{}
Put(interface{})
}
func testpool(name string, p pool, threads int) {
ops := int64(0)
allocs := int64(0)
running := true
starterPistol := make(chan int)
for i := 0; i < threads; i++ {
go func() {
_, _ = <-starterPistol
for running {
if v, ok := p.Get().([]byte); ok {
p.Put(v)
} else {
atomic.AddInt64(&allocs, 1)
p.Put(make([]byte, 4096))
}
atomic.AddInt64(&ops, 1)
}
}()
}
close(starterPistol)
time.Sleep(10 * time.Second)
running = false
fmt.Printf("%15s % 10d % 7d\n", name, ops, allocs)
runtime.GC()
time.Sleep(time.Second / 2)
runtime.GC()
time.Sleep(time.Second / 2)
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
fmt.Printf("%15s % 10s % 7s\n", "POOL", "OPS", "ALLOCS")
fmt.Println()
fmt.Println("(8 goroutines)")
testpool("sync.Pool", &sync.Pool{}, 8)
testpool("ChannelPool", NewChannelPool(8), 8)
testpool("MutexPool", NewMutexPool(8), 8)
testpool("AtomicPool", NewAtomicPool(8), 8)
testpool("AtomicPool2", NewAtomicPool2(8), 8)
fmt.Println()
fmt.Println("(16 goroutines)")
testpool("sync.Pool", &sync.Pool{}, 16)
testpool("ChannelPool", NewChannelPool(8), 16)
testpool("MutexPool", NewMutexPool(8), 16)
testpool("AtomicPool", NewAtomicPool(8), 16)
testpool("AtomicPool2", NewAtomicPool2(8), 16)
fmt.Println()
fmt.Println("(32 goroutines)")
testpool("sync.Pool", &sync.Pool{}, 32)
testpool("ChannelPool", NewChannelPool(8), 32)
testpool("MutexPool", NewMutexPool(8), 32)
testpool("AtomicPool", NewAtomicPool(8), 32)
testpool("AtomicPool2", NewAtomicPool2(8), 32)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment