Last active
September 13, 2018 16:51
-
-
Save lrita/360b08b26087e0665ae9262527dbbf5a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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