From 5b9a815a3e075778fa38d6a68f9ba527da2405a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?= Date: Fri, 26 Mar 2021 14:15:45 +0100 Subject: [PATCH] Add benchmark --- examples/runtime-stack/go.mod | 3 ++ examples/runtime-stack/main_test.go | 67 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 examples/runtime-stack/go.mod create mode 100644 examples/runtime-stack/main_test.go diff --git a/examples/runtime-stack/go.mod b/examples/runtime-stack/go.mod new file mode 100644 index 0000000..a5cc82a --- /dev/null +++ b/examples/runtime-stack/go.mod @@ -0,0 +1,3 @@ +module github.com/felixge/go-profiler-notes/examples/runtime-stack + +go 1.16 diff --git a/examples/runtime-stack/main_test.go b/examples/runtime-stack/main_test.go new file mode 100644 index 0000000..661cffd --- /dev/null +++ b/examples/runtime-stack/main_test.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "runtime" + "testing" + "time" +) + +func BenchmarkRuntimeStack(b *testing.B) { + buf := make([]byte, 1024*1024*64) + for g := 1; g <= 1024*1024; g = g * 2 { + g := g + name := fmt.Sprintf("%d goroutines", g) + + b.Run(name, func(b *testing.B) { + initalRoutines := runtime.NumGoroutine() + + readyCh := make(chan struct{}) + stopCh := make(chan struct{}) + for i := 0; i < g; i++ { + go atStackDepth(16, func() { + defer func() { stopCh <- struct{}{} }() + readyCh <- struct{}{} + }) + <-readyCh + } + + gotRoutines := runtime.NumGoroutine() - initalRoutines + if gotRoutines != g { + b.Logf("want %d goroutines, but got %d", g, gotRoutines) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + runtime.Stack(buf, true) + } + b.StopTimer() + for i := 0; i < g; i++ { + <-stopCh + } + start := time.Now() + for i := 0; ; i++ { + if runtime.NumGoroutine() == initalRoutines { + break + } + time.Sleep(20 * time.Millisecond) + if time.Since(start) > 10*time.Second { + b.Fatalf("%d goroutines still running, want %d", runtime.NumGoroutine(), initalRoutines) + } + } + }) + } +} + +func atStackDepth(depth int, fn func()) { + pcs := make([]uintptr, depth*10) + n := runtime.Callers(1, pcs) + if n > depth { + panic("depth exceeded") + } else if n < depth { + atStackDepth(depth, fn) + return + } + + fn() +}