aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/mgc.go
diff options
context:
space:
mode:
authorMichael Anthony Knyszek <mknyszek@google.com>2022-03-16 15:47:57 +0000
committerMichael Knyszek <mknyszek@google.com>2022-04-26 22:08:42 +0000
commite1b5f347e78c733bb0743df04c990e20f74bf188 (patch)
tree21f01ca7b096f632d97c7608f3aa470aff07521e /src/runtime/mgc.go
parent226346bb763233ed9341bc1d829752628479845f (diff)
downloadgo-e1b5f347e78c733bb0743df04c990e20f74bf188.tar.gz
go-e1b5f347e78c733bb0743df04c990e20f74bf188.zip
runtime: reduce max idle mark workers during periodic GC cycles
This change reduces the maximum number of idle mark workers during periodic (currently every 2 minutes) GC cycles to 1. Idle mark workers soak up all available and unused Ps, up to GOMAXPROCS. While this provides some throughput and latency benefit in general, it can cause what appear to be massive CPU utilization spikes in otherwise idle applications. This is mostly an issue for *very* idle applications, ones idle enough to trigger periodic GC cycles. This spike also tends to interact poorly with auto-scaling systems, as the system might assume the load average is very low and suddenly see a massive burst in activity. The result of this change is not to bring down this 100% (of GOMAXPROCS) CPU utilization spike to 0%, but rather min(25% + 1/GOMAXPROCS*100%, 100%) Idle mark workers also do incur a small latency penalty as they must be descheduled for other work that might pop up. Luckily the runtime is pretty good about getting idle mark workers off of Ps, so in general the latency benefit from shorter GC cycles outweighs this cost. But, the cost is still non-zero and may be more significant in idle applications that aren't invoking assists and write barriers quite as often. We can't completely eliminate idle mark workers because they're currently necessary for GC progress in some circumstances. Namely, they're critical for progress when all we have is fractional workers. If a fractional worker meets its quota, and all user goroutines are blocked directly or indirectly on a GC cycle (via runtime.GOMAXPROCS, or runtime.GC), the program may deadlock without GC workers, since the fractional worker will go to sleep with nothing to wake it. Fixes #37116. For #44163. Change-Id: Ib74793bb6b88d1765c52d445831310b0d11ef423 Reviewed-on: https://go-review.googlesource.com/c/go/+/393394 Reviewed-by: Michael Pratt <mpratt@google.com> Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
Diffstat (limited to 'src/runtime/mgc.go')
-rw-r--r--src/runtime/mgc.go6
1 files changed, 3 insertions, 3 deletions
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 9f17e47488..604d0db24a 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -672,7 +672,7 @@ func gcStart(trigger gcTrigger) {
// Assists and workers can start the moment we start
// the world.
- gcController.startCycle(now, int(gomaxprocs))
+ gcController.startCycle(now, int(gomaxprocs), trigger)
work.heapGoal = gcController.heapGoal
// In STW mode, disable scheduling of user Gs. This may also
@@ -1297,9 +1297,9 @@ func gcBgMarkWorker() {
casgstatus(gp, _Gwaiting, _Grunning)
})
- // Account for time.
+ // Account for time and mark us as stopped.
duration := nanotime() - startTime
- gcController.logWorkTime(pp.gcMarkWorkerMode, duration)
+ gcController.markWorkerStop(pp.gcMarkWorkerMode, duration)
if pp.gcMarkWorkerMode == gcMarkWorkerFractionalMode {
atomic.Xaddint64(&pp.gcFractionalMarkTime, duration)
}