diff --git a/fgprof.go b/fgprof.go index 31871c8..9940d4a 100644 --- a/fgprof.go +++ b/fgprof.go @@ -20,7 +20,7 @@ func Start(w io.Writer, format Format) func() error { ticker := time.NewTicker(time.Second / hz) stopCh := make(chan struct{}) - prof := &profiler{} + prof := newProfiler() stackCounts := stackCounter{} go func() { @@ -50,6 +50,16 @@ type profiler struct { selfFrame *runtime.Frame } +// newProfiler returns a new profiler with a pre-allocated stacks field. This +// is slightly faster than discovering its size during the first +// GoroutineProfile() call. +func newProfiler() *profiler { + n := runtime.NumGoroutine() + return &profiler{ + stacks: make([]runtime.StackRecord, int(float64(n)*1.1)), + } +} + // GoroutineProfile returns the stacks of all goroutines currently managed by // the scheduler. This includes both goroutines that are currently running // (On-CPU), as well as waiting (Off-CPU). diff --git a/fgprof_test.go b/fgprof_test.go index 658ca42..e847b82 100644 --- a/fgprof_test.go +++ b/fgprof_test.go @@ -3,6 +3,7 @@ package fgprof import ( "bytes" "fmt" + "runtime" "strings" "testing" "time" @@ -26,7 +27,7 @@ func TestStart(t *testing.T) { } func BenchmarkProfiler(b *testing.B) { - prof := &profiler{} + prof := newProfiler() for i := 0; i < b.N; i++ { prof.GoroutineProfile() } @@ -38,8 +39,7 @@ func BenchmarkProfilerGoroutines(b *testing.B) { name := fmt.Sprintf("%d goroutines", g) b.Run(name, func(b *testing.B) { - prof := &profiler{} - initalRoutines := len(prof.GoroutineProfile()) + initalRoutines := runtime.NumGoroutine() readyCh := make(chan struct{}) stopCh := make(chan struct{}) @@ -51,6 +51,9 @@ func BenchmarkProfilerGoroutines(b *testing.B) { <-readyCh } + // allocate profiler after some goroutines has been generated + prof := newProfiler() + b.ResetTimer() for i := 0; i < b.N; i++ { stacks := prof.GoroutineProfile() @@ -78,7 +81,7 @@ func BenchmarkProfilerGoroutines(b *testing.B) { } func BenchmarkStackCounter(b *testing.B) { - prof := &profiler{} + prof := newProfiler() stacks := prof.GoroutineProfile() sc := stackCounter{} b.ResetTimer()