Skip to content

Instantly share code, notes, and snippets.

@lrita
Last active September 13, 2018 16:51
Show Gist options
  • Save lrita/360b08b26087e0665ae9262527dbbf5a to your computer and use it in GitHub Desktop.
Save lrita/360b08b26087e0665ae9262527dbbf5a to your computer and use it in GitHub Desktop.
// This code is used for testing the cost of mutex contention.
// If there are a lot goroutines fetch a mutex simultaneously,
// those goroutines will be woke up slowly which are waiting for
// the mutex.
package main
import (
"flag"
"image"
"image/color"
"image/draw"
"image/png"
"os"
"runtime"
"sort"
"sync"
"time"
"unsafe"
)
type obj struct {
beginlock int64
locked int64
beginunlock int64
unlocked int64
}
type objx struct {
obj
_ [64 - unsafe.Sizeof(obj{})]byte
}
func main() {
var (
num int
threshold time.Duration
work time.Duration
mu sync.Mutex
wg sync.WaitGroup
xg0, xg1 sync.WaitGroup
)
flag.IntVar(&num, "n", runtime.NumCPU(), "num p")
flag.DurationVar(&threshold, "t", 5*time.Millisecond, "timeout threshold")
flag.DurationVar(&work, "w", time.Millisecond, "work duration")
flag.Parse()
const imgcnt = 10 // image count
gslice := make([][]objx, 0, imgcnt)
for j := 0; j < imgcnt; j++ {
slice := make([]objx, num)
xg0.Add(1)
for i := 0; i < num; i++ {
wg.Add(1)
xg1.Add(1)
go func(o *objx) {
defer wg.Done()
xg1.Done()
xg0.Wait()
o.beginlock = time.Now().UnixNano()
mu.Lock()
o.locked = time.Now().UnixNano()
if work != 0 {
time.Sleep(work)
}
o.beginunlock = time.Now().UnixNano()
mu.Unlock()
o.unlocked = time.Now().UnixNano()
}(&slice[i])
}
wg.Add(1)
go func() {
defer wg.Done()
xg1.Wait()
xg0.Done()
}()
wg.Wait()
gslice = append(gslice, slice)
}
const interval = 2
const imginterval = 10
const height = 2
const width = 1000
blue := image.NewUniform(color.RGBA{0, 0, 255, 255})
red := image.NewUniform(color.RGBA{255, 0, 0, 255})
img := image.NewRGBA(image.Rect(0, 0, width, (num*height+(num-1)*interval)*len(gslice)+imginterval*(len(gslice)-1)))
for idx, slice := range gslice {
sort.Slice(slice, func(i, j int) bool {
return slice[i].beginunlock < slice[j].beginunlock
})
begin := slice[0].beginlock
for i, s := range slice {
length := s.unlocked - begin
h0 := idx*(imginterval+num*(height)+(num-1)*interval) + i*(height+interval)
h1 := h0 + height
lockx0 := (s.beginlock - begin) * width / length
lockx1 := (s.locked - begin) * width / length
unlockx0 := (s.beginunlock - begin) * width / length
unlockx1 := (s.unlocked - begin) * width / length
// fill to white
draw.Draw(img, image.Rect(0, h0, width, h1), image.White, image.ZP, draw.Src)
// lock stage fill blue
draw.Draw(img, image.Rect(int(lockx0), h0, int(lockx1), h1), blue, image.ZP, draw.Src)
draw.Draw(img, image.Rect(int(lockx1), h0, int(unlockx0), h1), red, image.ZP, draw.Src)
draw.Draw(img, image.Rect(int(unlockx0), h0, int(unlockx1), h1), image.Black, image.ZP, draw.Src)
begin = s.beginunlock
}
}
file, err := os.OpenFile("img.png", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
if err != nil {
panic(err)
}
if err := png.Encode(file, img); err != nil {
panic(err)
}
file.Close()
}
// This code is used for testing the cost of mutex contention.
// If there are a lot goroutines fetch a mutex simultaneously,
// those goroutines will be woke up slowly which are waiting for
// the mutex.
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"runtime"
"sync"
"syscall"
"time"
"unsafe"
)
type obj struct {
total int64
timeout int64
cost time.Duration
icost time.Duration
maxcost time.Duration
maxicost time.Duration
max time.Duration
unmax time.Duration
}
type objx struct {
obj
_ [64 - unsafe.Sizeof(obj{})]byte
}
func main() {
var (
num int
total int64
timeout int64
max time.Duration
threshold time.Duration
work time.Duration
testtime time.Duration
mu sync.Mutex
wg sync.WaitGroup
xg0, xg1 sync.WaitGroup
)
flag.IntVar(&num, "n", runtime.NumCPU(), "num p")
flag.DurationVar(&threshold, "t", 5*time.Millisecond, "timeout threshold")
flag.DurationVar(&testtime, "tt", 15*time.Second, "test time")
flag.DurationVar(&work, "w", time.Millisecond, "work duration")
flag.Parse()
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM, os.Interrupt)
fired := time.After(testtime)
slice := make([]objx, num)
Exit:
for {
xg0.Add(1)
for i := 0; i < num; i++ {
wg.Add(1)
xg1.Add(1)
go func(o *objx) {
defer wg.Done()
xg1.Done()
xg0.Wait()
begin := time.Now()
mu.Lock()
inbeg := time.Now()
o.total++
if since := inbeg.Sub(begin); since > threshold {
o.timeout++
if since > o.max {
o.max = since
}
}
if work != 0 {
time.Sleep(work)
}
inend := time.Now()
mu.Unlock()
end := time.Now()
icost := inend.Sub(inbeg)
o.icost += icost
if icost > o.maxicost {
o.maxicost = icost
}
cost := end.Sub(begin)
o.cost += cost
if cost > o.maxcost {
o.maxcost = cost
}
if unmax := end.Sub(inend); unmax > o.unmax {
o.unmax = unmax
}
}(&slice[i])
}
wg.Add(1)
go func() {
defer wg.Done()
xg1.Wait()
xg0.Done()
}()
wg.Wait()
select {
case <-fired:
fmt.Println("time reached exit")
break Exit
case <-sig:
fmt.Println("exit")
break Exit
default:
}
}
var sumcost, sumicost, unmax, maxcost, maxicost time.Duration
for _, o := range slice {
total += o.total
timeout += o.timeout
sumcost += o.cost
sumicost += o.icost
if o.max > max {
max = o.max
}
if o.unmax > unmax {
unmax = o.unmax
}
if o.maxcost > maxcost {
maxcost = o.maxcost
}
if o.maxicost > maxicost {
maxicost = o.maxicost
}
}
fmt.Println("totoal: ", total, "timeout: ", timeout,
"percent: ", float64(timeout)/float64(total), "max: ", max,
"unlock max: ", unmax, "max cost :", maxcost, "max mutex internal cost: ", maxicost,
"avg cost: ", sumcost/time.Duration(total),
"avg mutex internal cost: ", sumicost/time.Duration(total))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment