From 315c28b788285e1a683057e4a90eacbfc401d1be Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 4 Oct 2017 17:07:09 -0400 Subject: [PATCH] runtime: use only dedicated mark workers at reasonable GOMAXPROCS When GOMAXPROCS is not small, fractional workers don't add much to throughput, but they do add to the latency of individual goroutines. In this case, it makes sense to just use dedicated workers, even if we can't exactly hit the 25% CPU goal with dedicated workers. This implements this logic by computing the number of dedicated mark workers that will us closest to the 25% target. We only fall back to fractional workers if that would be more than 30% off of the target (less than 17.5% or more than 32.5%, which in practice happens for GOMAXPROCS <= 3 and GOMAXPROCS == 6). Updates #21698. Change-Id: I484063adeeaa1190200e4ef210193a20e635d552 Reviewed-on: https://go-review.googlesource.com/68571 Reviewed-by: Rick Hudson --- src/runtime/mgc.go | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 32383736ec..83eb14f772 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -453,11 +453,28 @@ func (c *gcControllerState) startCycle() { memstats.next_gc = memstats.heap_live + 1024*1024 } - // Compute the background mark utilization goal and divide it among - // dedicated and fractional workers. + // Compute the background mark utilization goal. In general, + // this may not come out exactly. We round the number of + // dedicated workers so that the utilization is closest to + // 25%. For small GOMAXPROCS, this would introduce too much + // error, so we add fractional workers in that case. totalUtilizationGoal := float64(gomaxprocs) * gcBackgroundUtilization - c.dedicatedMarkWorkersNeeded = int64(totalUtilizationGoal) - c.fractionalUtilizationGoal = totalUtilizationGoal - float64(c.dedicatedMarkWorkersNeeded) + c.dedicatedMarkWorkersNeeded = int64(totalUtilizationGoal + 0.5) + utilError := float64(c.dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1 + const maxUtilError = 0.3 + if utilError < -maxUtilError || utilError > maxUtilError { + // Rounding put us more than 30% off our goal. With + // gcBackgroundUtilization of 25%, this happens for + // GOMAXPROCS<=3 or GOMAXPROCS=6. Enable fractional + // workers to compensate. + if float64(c.dedicatedMarkWorkersNeeded) > totalUtilizationGoal { + // Too many dedicated workers. + c.dedicatedMarkWorkersNeeded-- + } + c.fractionalUtilizationGoal = totalUtilizationGoal - float64(c.dedicatedMarkWorkersNeeded) + } else { + c.fractionalUtilizationGoal = 0 + } if c.fractionalUtilizationGoal > 0 { c.fractionalMarkWorkersNeeded = 1 } else { @@ -863,7 +880,8 @@ const gcGoalUtilization = 0.25 // gcBackgroundUtilization is the fixed CPU utilization for background // marking. It must be <= gcGoalUtilization. The difference between // gcGoalUtilization and gcBackgroundUtilization will be made up by -// mark assists. +// mark assists. The scheduler will aim to use within 50% of this +// goal. const gcBackgroundUtilization = 0.25 // gcCreditSlack is the amount of scan work credit that can can