Skip to content

Instantly share code, notes, and snippets.

@craigmj
Created June 13, 2013 01:07
Show Gist options
  • Save craigmj/5770480 to your computer and use it in GitHub Desktop.
Save craigmj/5770480 to your computer and use it in GitHub Desktop.

Revisions

  1. craigmj created this gist Jun 13, 2013.
    201 changes: 201 additions & 0 deletions gomaps.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,201 @@
    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()
    }