1
0
mirror of https://github.com/golang/go synced 2024-11-23 14:40:02 -07:00

runtime: convert gcController.dedicatedMarkWorkersNeeded to atomic type

In gcController.startCycle we just compute the initial value in a
local variable before assigning to the atomic field to avoid noisy
churn.

For #53821.

Change-Id: Ibde0ac8fd49aa6bbee3bd02fe3ffb17429abd5a9
Reviewed-on: https://go-review.googlesource.com/c/go/+/417784
Run-TryBot: Michael Pratt <mpratt@google.com>
Reviewed-by: Austin Clements <austin@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Michael Pratt 2022-07-14 17:15:44 -04:00
parent dcd10375bc
commit e76155614a
2 changed files with 20 additions and 25 deletions

View File

@ -21,7 +21,6 @@ var AtomicFields = []uintptr{
unsafe.Offsetof(schedt{}.lastpoll), unsafe.Offsetof(schedt{}.lastpoll),
unsafe.Offsetof(schedt{}.pollUntil), unsafe.Offsetof(schedt{}.pollUntil),
unsafe.Offsetof(schedt{}.timeToRun), unsafe.Offsetof(schedt{}.timeToRun),
unsafe.Offsetof(gcControllerState{}.dedicatedMarkWorkersNeeded),
unsafe.Offsetof(timeHistogram{}.underflow), unsafe.Offsetof(timeHistogram{}.underflow),
unsafe.Offsetof(profBuf{}.overflow), unsafe.Offsetof(profBuf{}.overflow),
unsafe.Offsetof(profBuf{}.overflowTime), unsafe.Offsetof(profBuf{}.overflowTime),

View File

@ -92,8 +92,6 @@ type gcControllerState struct {
// Initialized from GOGC. GOGC=off means no GC. // Initialized from GOGC. GOGC=off means no GC.
gcPercent atomic.Int32 gcPercent atomic.Int32
_ uint32 // padding so following 64-bit values are 8-byte aligned
// memoryLimit is the soft memory limit in bytes. // memoryLimit is the soft memory limit in bytes.
// //
// Initialized from GOMEMLIMIT. GOMEMLIMIT=off is equivalent to MaxInt64 // Initialized from GOMEMLIMIT. GOMEMLIMIT=off is equivalent to MaxInt64
@ -145,8 +143,6 @@ type gcControllerState struct {
// consMark; see consMark for details. // consMark; see consMark for details.
consMarkController piController consMarkController piController
_ uint32 // Padding for atomics on 32-bit platforms.
// gcPercentHeapGoal is the goal heapLive for when next GC ends derived // gcPercentHeapGoal is the goal heapLive for when next GC ends derived
// from gcPercent. // from gcPercent.
// //
@ -289,11 +285,10 @@ type gcControllerState struct {
// that assists and background mark workers started. // that assists and background mark workers started.
markStartTime int64 markStartTime int64
// dedicatedMarkWorkersNeeded is the number of dedicated mark // dedicatedMarkWorkersNeeded is the number of dedicated mark workers
// workers that need to be started. This is computed at the // that need to be started. This is computed at the beginning of each
// beginning of each cycle and decremented atomically as // cycle and decremented as dedicated mark workers get started.
// dedicated mark workers get started. dedicatedMarkWorkersNeeded atomic.Int64
dedicatedMarkWorkersNeeded int64
// idleMarkWorkers is two packed int32 values in a single uint64. // idleMarkWorkers is two packed int32 values in a single uint64.
// These two values are always updated simultaneously. // These two values are always updated simultaneously.
@ -448,26 +443,26 @@ func (c *gcControllerState) startCycle(markStartTime int64, procs int, trigger g
// 25%. For small GOMAXPROCS, this would introduce too much // 25%. For small GOMAXPROCS, this would introduce too much
// error, so we add fractional workers in that case. // error, so we add fractional workers in that case.
totalUtilizationGoal := float64(procs) * gcBackgroundUtilization totalUtilizationGoal := float64(procs) * gcBackgroundUtilization
c.dedicatedMarkWorkersNeeded = int64(totalUtilizationGoal + 0.5) dedicatedMarkWorkersNeeded := int64(totalUtilizationGoal + 0.5)
utilError := float64(c.dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1 utilError := float64(dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1
const maxUtilError = 0.3 const maxUtilError = 0.3
if utilError < -maxUtilError || utilError > maxUtilError { if utilError < -maxUtilError || utilError > maxUtilError {
// Rounding put us more than 30% off our goal. With // Rounding put us more than 30% off our goal. With
// gcBackgroundUtilization of 25%, this happens for // gcBackgroundUtilization of 25%, this happens for
// GOMAXPROCS<=3 or GOMAXPROCS=6. Enable fractional // GOMAXPROCS<=3 or GOMAXPROCS=6. Enable fractional
// workers to compensate. // workers to compensate.
if float64(c.dedicatedMarkWorkersNeeded) > totalUtilizationGoal { if float64(dedicatedMarkWorkersNeeded) > totalUtilizationGoal {
// Too many dedicated workers. // Too many dedicated workers.
c.dedicatedMarkWorkersNeeded-- dedicatedMarkWorkersNeeded--
} }
c.fractionalUtilizationGoal = (totalUtilizationGoal - float64(c.dedicatedMarkWorkersNeeded)) / float64(procs) c.fractionalUtilizationGoal = (totalUtilizationGoal - float64(dedicatedMarkWorkersNeeded)) / float64(procs)
} else { } else {
c.fractionalUtilizationGoal = 0 c.fractionalUtilizationGoal = 0
} }
// In STW mode, we just want dedicated workers. // In STW mode, we just want dedicated workers.
if debug.gcstoptheworld > 0 { if debug.gcstoptheworld > 0 {
c.dedicatedMarkWorkersNeeded = int64(procs) dedicatedMarkWorkersNeeded = int64(procs)
c.fractionalUtilizationGoal = 0 c.fractionalUtilizationGoal = 0
} }
@ -482,7 +477,7 @@ func (c *gcControllerState) startCycle(markStartTime int64, procs int, trigger g
// required. However, we need at least one dedicated mark worker or // required. However, we need at least one dedicated mark worker or
// idle GC worker to ensure GC progress in some scenarios (see comment // idle GC worker to ensure GC progress in some scenarios (see comment
// on maxIdleMarkWorkers). // on maxIdleMarkWorkers).
if c.dedicatedMarkWorkersNeeded > 0 { if dedicatedMarkWorkersNeeded > 0 {
c.setMaxIdleMarkWorkers(0) c.setMaxIdleMarkWorkers(0)
} else { } else {
// TODO(mknyszek): The fundamental reason why we need this is because // TODO(mknyszek): The fundamental reason why we need this is because
@ -492,13 +487,14 @@ func (c *gcControllerState) startCycle(markStartTime int64, procs int, trigger g
c.setMaxIdleMarkWorkers(1) c.setMaxIdleMarkWorkers(1)
} }
} else { } else {
// N.B. gomaxprocs and dedicatedMarkWorkersNeeded is guaranteed not to // N.B. gomaxprocs and dedicatedMarkWorkersNeeded are guaranteed not to
// change during a GC cycle. // change during a GC cycle.
c.setMaxIdleMarkWorkers(int32(procs) - int32(c.dedicatedMarkWorkersNeeded)) c.setMaxIdleMarkWorkers(int32(procs) - int32(dedicatedMarkWorkersNeeded))
} }
// Compute initial values for controls that are updated // Compute initial values for controls that are updated
// throughout the cycle. // throughout the cycle.
c.dedicatedMarkWorkersNeeded.Store(dedicatedMarkWorkersNeeded)
c.revise() c.revise()
if debug.gcpacertrace > 0 { if debug.gcpacertrace > 0 {
@ -507,7 +503,7 @@ func (c *gcControllerState) startCycle(markStartTime int64, procs int, trigger g
" (scan ", gcController.heapScan.Load()>>20, " MB in ", " (scan ", gcController.heapScan.Load()>>20, " MB in ",
work.initialHeapLive>>20, "->", work.initialHeapLive>>20, "->",
heapGoal>>20, " MB)", heapGoal>>20, " MB)",
" workers=", c.dedicatedMarkWorkersNeeded, " workers=", dedicatedMarkWorkersNeeded,
"+", c.fractionalUtilizationGoal, "\n") "+", c.fractionalUtilizationGoal, "\n")
} }
} }
@ -761,7 +757,7 @@ func (c *gcControllerState) enlistWorker() {
// There are no idle Ps. If we need more dedicated workers, // There are no idle Ps. If we need more dedicated workers,
// try to preempt a running P so it will switch to a worker. // try to preempt a running P so it will switch to a worker.
if c.dedicatedMarkWorkersNeeded <= 0 { if c.dedicatedMarkWorkersNeeded.Load() <= 0 {
return return
} }
// Pick a random other P to preempt. // Pick a random other P to preempt.
@ -831,14 +827,14 @@ func (c *gcControllerState) findRunnableGCWorker(pp *p, now int64) (*g, int64) {
return nil, now return nil, now
} }
decIfPositive := func(ptr *int64) bool { decIfPositive := func(val *atomic.Int64) bool {
for { for {
v := atomic.Loadint64(ptr) v := val.Load()
if v <= 0 { if v <= 0 {
return false return false
} }
if atomic.Casint64(ptr, v, v-1) { if val.CompareAndSwap(v, v-1) {
return true return true
} }
} }
@ -905,7 +901,7 @@ func (c *gcControllerState) markWorkerStop(mode gcMarkWorkerMode, duration int64
switch mode { switch mode {
case gcMarkWorkerDedicatedMode: case gcMarkWorkerDedicatedMode:
c.dedicatedMarkTime.Add(duration) c.dedicatedMarkTime.Add(duration)
atomic.Xaddint64(&c.dedicatedMarkWorkersNeeded, 1) c.dedicatedMarkWorkersNeeded.Add(1)
case gcMarkWorkerFractionalMode: case gcMarkWorkerFractionalMode:
c.fractionalMarkTime.Add(duration) c.fractionalMarkTime.Add(duration)
case gcMarkWorkerIdleMode: case gcMarkWorkerIdleMode: