Skip to content

Commit 23e4744

Browse files
committed
runtime: report GC CPU utilization in MemStats
This adds a GCCPUFraction field to MemStats that reports the cumulative fraction of the program's execution time spent in the garbage collector. This is equivalent to the utilization percent shown in the gctrace output and makes this available programmatically. This does make one small effect on the gctrace output: we now report the duration of mark termination up to just before the final start-the-world, rather than up to just after. However, unlike stop-the-world, I don't believe there's any way that start-the-world can block, so it should take negligible time. While there are many statistics one might want to expose via MemStats, this is one of the few that will undoubtedly remain meaningful regardless of future changes to the memory system. The diff for this change is larger than the actual change. Mostly it lifts the code for computing the GC CPU utilization out of the debug.gctrace path. Updates #10323. Change-Id: I0f7dc3fdcafe95e8d1233ceb79de606b48acd989 Reviewed-on: https://go-review.googlesource.com/12844 Reviewed-by: Russ Cox <[email protected]>
1 parent 4b71660 commit 23e4744

File tree

2 files changed

+36
-33
lines changed

2 files changed

+36
-33
lines changed

src/runtime/mgc.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,21 @@ func gc(mode int) {
11211121
memstats.pause_end[memstats.numgc%uint32(len(memstats.pause_end))] = uint64(unixNow)
11221122
memstats.pause_total_ns += uint64(pauseNS)
11231123

1124+
// Update work.totaltime.
1125+
sweepTermCpu := int64(stwprocs) * (tScan - tSweepTerm)
1126+
scanCpu := tInstallWB - tScan
1127+
installWBCpu := int64(0)
1128+
// We report idle marking time below, but omit it from the
1129+
// overall utilization here since it's "free".
1130+
markCpu := gcController.assistTime + gcController.dedicatedMarkTime + gcController.fractionalMarkTime
1131+
markTermCpu := int64(stwprocs) * (now - tMarkTerm)
1132+
cycleCpu := sweepTermCpu + scanCpu + installWBCpu + markCpu + markTermCpu
1133+
work.totaltime += cycleCpu
1134+
1135+
// Compute overall GC CPU utilization.
1136+
totalCpu := sched.totaltime + (now-sched.procresizetime)*int64(gomaxprocs)
1137+
memstats.gc_cpu_fraction = float64(work.totaltime) / float64(totalCpu)
1138+
11241139
memstats.numgc++
11251140

11261141
systemstack(startTheWorldWithSema)
@@ -1130,22 +1145,8 @@ func gc(mode int) {
11301145
mp = nil
11311146

11321147
if debug.gctrace > 0 {
1133-
tEnd := nanotime()
1134-
1135-
// Update work.totaltime
1136-
sweepTermCpu := int64(stwprocs) * (tScan - tSweepTerm)
1137-
scanCpu := tInstallWB - tScan
1138-
installWBCpu := int64(0)
1139-
// We report idle marking time below, but omit it from
1140-
// the overall utilization here since it's "free".
1141-
markCpu := gcController.assistTime + gcController.dedicatedMarkTime + gcController.fractionalMarkTime
1142-
markTermCpu := int64(stwprocs) * (tEnd - tMarkTerm)
1143-
cycleCpu := sweepTermCpu + scanCpu + installWBCpu + markCpu + markTermCpu
1144-
work.totaltime += cycleCpu
1145-
1146-
// Compute overall utilization
1147-
totalCpu := sched.totaltime + (tEnd-sched.procresizetime)*int64(gomaxprocs)
1148-
util := work.totaltime * 100 / totalCpu
1148+
tEnd := now
1149+
util := int(memstats.gc_cpu_fraction * 100)
11491150

11501151
var sbuf [24]byte
11511152
printlock()

src/runtime/mstats.go

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ package runtime
99
import "unsafe"
1010

1111
// Statistics.
12-
// Shared with Go: if you edit this structure, also edit type MemStats in mem.go.
12+
// If you edit this structure, also edit type MemStats below.
1313
type mstats struct {
1414
// General statistics.
1515
alloc uint64 // bytes allocated and not yet freed
@@ -42,14 +42,15 @@ type mstats struct {
4242

4343
// Statistics about garbage collector.
4444
// Protected by mheap or stopping the world during GC.
45-
next_gc uint64 // next gc (in heap_alloc time)
46-
last_gc uint64 // last gc (in absolute time)
47-
pause_total_ns uint64
48-
pause_ns [256]uint64 // circular buffer of recent gc pause lengths
49-
pause_end [256]uint64 // circular buffer of recent gc end times (nanoseconds since 1970)
50-
numgc uint32
51-
enablegc bool
52-
debuggc bool
45+
next_gc uint64 // next gc (in heap_alloc time)
46+
last_gc uint64 // last gc (in absolute time)
47+
pause_total_ns uint64
48+
pause_ns [256]uint64 // circular buffer of recent gc pause lengths
49+
pause_end [256]uint64 // circular buffer of recent gc end times (nanoseconds since 1970)
50+
numgc uint32
51+
gc_cpu_fraction float64 // fraction of CPU time used by GC
52+
enablegc bool
53+
debuggc bool
5354

5455
// Statistics about allocation size classes.
5556

@@ -119,14 +120,15 @@ type MemStats struct {
119120
OtherSys uint64 // other system allocations
120121

121122
// Garbage collector statistics.
122-
NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount
123-
LastGC uint64 // end time of last collection (nanoseconds since 1970)
124-
PauseTotalNs uint64
125-
PauseNs [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
126-
PauseEnd [256]uint64 // circular buffer of recent GC pause end times
127-
NumGC uint32
128-
EnableGC bool
129-
DebugGC bool
123+
NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount
124+
LastGC uint64 // end time of last collection (nanoseconds since 1970)
125+
PauseTotalNs uint64
126+
PauseNs [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
127+
PauseEnd [256]uint64 // circular buffer of recent GC pause end times
128+
NumGC uint32
129+
GCCPUFraction float64 // fraction of CPU time used by GC
130+
EnableGC bool
131+
DebugGC bool
130132

131133
// Per-size allocation statistics.
132134
// 61 is NumSizeClasses in the C code.

0 commit comments

Comments
 (0)