Skip to content

Instantly share code, notes, and snippets.

@life1347
Forked from craigmj/gomaps.go
Created August 24, 2018 08:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save life1347/f049488f929317e91a168ac0e3826c04 to your computer and use it in GitHub Desktop.
Save life1347/f049488f929317e91a168ac0e3826c04 to your computer and use it in GitHub Desktop.
Performance testing of goroutine vs sync map implementation in Go.
package main
import (
"fmt"
"math/rand"
"runtime"
"strconv"
"sync"
"time"
)
type Map interface {
Get(key string) (interface{}, bool)
Set(key string, value interface{})
}
///////////////////////////////// GO ROUTINE BASED MAP ////////////////////////////////////////
type mapResult struct {
value interface{}
ok bool
}
type mapGet struct {
key string
out chan mapResult
}
type mapSet struct {
key string
value interface{}
}
type GoMap struct {
get chan mapGet
set chan mapSet
done chan bool
m map[string]interface{}
}
func NewGoMap() *GoMap {
g := &GoMap{
get: make(chan mapGet),
set: make(chan mapSet),
done: make(chan bool),
m: make(map[string]interface{}),
}
go g.run()
return g
}
func (g *GoMap) run() {
defer func() { g.done <- true }()
for {
select {
case r, ok := <-g.get:
if !ok {
return
}
value, ok := g.m[r.key]
r.out <- mapResult{value, ok}
case r, ok := <-g.set:
if !ok {
return
}
g.m[r.key] = r.value
}
}
}
func (g *GoMap) Stop() {
close(g.get)
close(g.set)
<-g.done
}
func (g *GoMap) Get(key string) (interface{}, bool) {
c := make(chan mapResult)
g.get <- mapGet{key, c}
r := <-c
return r.value, r.ok
}
func (g *GoMap) Set(key string, value interface{}) {
g.set <- mapSet{key, value}
}
///////////////////////////////// SINGLE CHANNEL GO ROUTINE BASED MAP /////////////////////////
type GoMap1Chan struct {
in chan interface{}
done chan bool
m map[string]interface{}
}
func NewGoMap1Chan() *GoMap1Chan {
g := &GoMap1Chan{in: make(chan interface{}), done: make(chan bool), m: make(map[string]interface{})}
go g.run()
return g
}
func (g *GoMap1Chan) run() {
defer func() { g.done <- true }()
for i := range g.in {
switch r := i.(type) {
case mapGet:
value, ok := g.m[r.key]
r.out <- mapResult{value, ok}
case mapSet:
g.m[r.key] = r.value
default:
panic("Unknown type on GoMap1Chan in")
}
}
}
func (g *GoMap1Chan) Stop() {
close(g.in)
<-g.done
}
func (g *GoMap1Chan) Get(key string) (interface{}, bool) {
c := make(chan mapResult)
g.in <- mapGet{key, c}
r := <-c
return r.value, r.ok
}
func (g *GoMap1Chan) Set(key string, value interface{}) {
g.in <- mapSet{key, value}
}
//////////////////////////////////// SYNC BASED MAP //////////////////////////////////
type SyncMap struct {
lock sync.RWMutex
m map[string]interface{}
}
func NewSyncMap() *SyncMap {
return &SyncMap{m: make(map[string]interface{})}
}
func (s *SyncMap) Get(key string) (interface{}, bool) {
s.lock.RLock()
defer s.lock.RUnlock()
value, ok := s.m[key]
return value, ok
}
func (s *SyncMap) Set(key string, value interface{}) {
s.lock.Lock()
defer s.lock.Unlock()
s.m[key] = value
}
//////////////////////////////////// THE TESTING CODE ////////////////////////////////
func TheTest(g Map, rnd *rand.Rand) time.Duration {
start := time.Now()
var key string
var value string
var got interface{}
for i := 0; i < 100000; i++ {
key = strconv.Itoa(int(rnd.Int31n(500)))
value = "The value " + key
g.Set(key, value)
got, _ = g.Get(key)
if value != got {
panic(fmt.Sprintf("ERROR: expected %v, got %v", value, got))
}
}
return time.Now().Sub(start)
}
func TestInParallel(g Map, n int) time.Duration {
start := time.Now()
var wait sync.WaitGroup
for i := 0; i < n; i++ {
wait.Add(1)
go func() {
TheTest(g, rand.New(rand.NewSource(time.Now().Unix()+int64(i*500))))
wait.Done()
}()
}
wait.Wait()
return time.Now().Sub(start)
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
gm := NewGoMap()
gm1chan := NewGoMap1Chan()
sm := NewSyncMap()
nRoutines := 10
fmt.Println("In parallel on", runtime.NumCPU(), "CPUs with", nRoutines, "goroutines")
fmt.Println("GoMap: ", TestInParallel(gm, nRoutines))
fmt.Println("GoMap1Chan: ", TestInParallel(gm1chan, nRoutines))
fmt.Println("SyncMap: ", TestInParallel(sm, nRoutines))
gm.Stop()
gm1chan.Stop()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment