diff --git a/examples/block-vs-mutex/main.go b/examples/block-vs-mutex/main.go new file mode 100644 index 0000000..5c0e336 --- /dev/null +++ b/examples/block-vs-mutex/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "os" + "runtime" + "runtime/pprof" + "sync" + "time" +) + +func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run() error { + runtime.SetBlockProfileRate(1) + runtime.SetMutexProfileFraction(1) + + aquired := make(chan struct{}) + var m sync.Mutex + m.Lock() + go func() { + <-aquired + m.Lock() + aquired <- struct{}{} + }() + aquired <- struct{}{} + time.Sleep(time.Nanosecond) + m.Unlock() + <-aquired + + if err := writeProfile("block"); err != nil { + return err + } else if err := writeProfile("mutex"); err != nil { + return err + } + return nil +} + +func writeProfile(name string) error { + f, err := os.Create(name + ".pb.gz") + if err != nil { + return err + } + defer f.Close() + + if err := pprof.Lookup(name).WriteTo(f, 0); err != nil { + return err + } + return nil +} diff --git a/examples/block/main b/examples/block/main new file mode 100755 index 0000000..6bb6bd6 Binary files /dev/null and b/examples/block/main differ diff --git a/examples/block/main.go b/examples/block/main.go new file mode 100644 index 0000000..d5bc400 --- /dev/null +++ b/examples/block/main.go @@ -0,0 +1,76 @@ +package main + +import ( + "os" + "runtime" + "runtime/pprof" + "sync" + "time" +) + +var foo []string + +func main() { + demonstrateSleep() + + f, err := os.Create("block.pb.gz") + if err != nil { + panic(err) + } + defer f.Close() + if err := pprof.Lookup("block").WriteTo(f, 0); err != nil { + panic(err) + } +} + +func demonstrateSleep() { + runtime.SetBlockProfileRate(1) + <-time.After(time.Millisecond) +} + +func demonstrateSelect() { + runtime.SetBlockProfileRate(1) + + ch1 := make(chan struct{}, 0) + ch2 := make(chan struct{}, 1) + ch3 := make(chan struct{}, 0) + + go func() { + ch2 <- struct{}{} + }() + + time.Sleep(20 * time.Millisecond) + + select { + case <-ch1: + case <-ch2: + case <-ch3: + } +} + +func demonstrateSampling() { + runtime.SetBlockProfileRate(int(40 * time.Microsecond.Nanoseconds())) + for i := 0; i < 10000; i++ { + blockMutex(10 * time.Microsecond) + } +} + +func blockMutex(d time.Duration) { + m := &sync.Mutex{} + m.Lock() + go func() { + spinSleep(d) + m.Unlock() + }() + m.Lock() +} + +// spinSleep is a more accurate version of time.Sleep() for short sleep +// durations. Accuracy seems to be ~35ns. +func spinSleep(d time.Duration) { + start := time.Now() + n := 0 + for time.Since(start) < d { + n++ + } +}