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:
parent
12d1c9b863
commit
1f0738c157
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user