1
0
mirror of https://github.com/golang/go synced 2024-11-18 21:05:02 -07:00

runtime/pprof: speed up CPU profiling shutdown

The core CPU profiling loop contains a 100ms sleep.
This is important to reduce overhead.

However, it means that it takes 200ms to shutting down a program
with CPU profiling enabled. When trying to collect many samples
by running a short-lived program many times, this adds up.

This change cuts the shutdown penalty in half by skipping
the sleep whenever possible.

Change-Id: Ic3177f8e1a2d331fe1a1ecd7c8c06f50beb42535
Reviewed-on: https://go-review.googlesource.com/c/go/+/228886
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
Josh Bleecher Snyder 2019-12-31 21:24:20 -08:00
parent 12d1c9b863
commit 1f0738c157

View File

@ -81,6 +81,7 @@ import (
"sort" "sort"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"text/tabwriter" "text/tabwriter"
"time" "time"
"unsafe" "unsafe"
@ -714,10 +715,22 @@ func (p runtimeProfile) Stack(i int) []uintptr { return p[i].Stack() }
var cpu struct { var cpu struct {
sync.Mutex sync.Mutex
profiling bool profiling uint32 // bool, accessed atomically
done chan bool done chan bool
} }
func cpuProfiling() bool {
return atomic.LoadUint32(&cpu.profiling) != 0
}
func setCPUProfiling(b bool) {
if b {
atomic.StoreUint32(&cpu.profiling, 1)
} else {
atomic.StoreUint32(&cpu.profiling, 0)
}
}
// StartCPUProfile enables CPU profiling for the current process. // StartCPUProfile enables CPU profiling for the current process.
// While profiling, the profile will be buffered and written to w. // While profiling, the profile will be buffered and written to w.
// StartCPUProfile returns an error if profiling is already enabled. // StartCPUProfile returns an error if profiling is already enabled.
@ -747,10 +760,10 @@ func StartCPUProfile(w io.Writer) error {
cpu.done = make(chan bool) cpu.done = make(chan bool)
} }
// Double-check. // Double-check.
if cpu.profiling { if cpuProfiling() {
return fmt.Errorf("cpu profiling already in use") return fmt.Errorf("cpu profiling already in use")
} }
cpu.profiling = true setCPUProfiling(true)
runtime.SetCPUProfileRate(hz) runtime.SetCPUProfileRate(hz)
go profileWriter(w) go profileWriter(w)
return nil return nil
@ -767,7 +780,9 @@ func profileWriter(w io.Writer) {
b := newProfileBuilder(w) b := newProfileBuilder(w)
var err error var err error
for { for {
if cpuProfiling() {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
}
data, tags, eof := readProfile() data, tags, eof := readProfile()
if e := b.addCPUData(data, tags); e != nil && err == nil { if e := b.addCPUData(data, tags); e != nil && err == nil {
err = e err = e
@ -792,10 +807,10 @@ func StopCPUProfile() {
cpu.Lock() cpu.Lock()
defer cpu.Unlock() defer cpu.Unlock()
if !cpu.profiling { if !cpuProfiling() {
return return
} }
cpu.profiling = false setCPUProfiling(false)
runtime.SetCPUProfileRate(0) runtime.SetCPUProfileRate(0)
<-cpu.done <-cpu.done
} }