]> Cypherpunks.ru repositories - gostls13.git/commitdiff
runtime/pprof: speed up CPU profiling shutdown
authorJosh Bleecher Snyder <josharian@gmail.com>
Wed, 1 Jan 2020 05:24:20 +0000 (21:24 -0800)
committerJosh Bleecher Snyder <josharian@gmail.com>
Mon, 20 Apr 2020 15:52:05 +0000 (15:52 +0000)
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>
src/runtime/pprof/pprof.go

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