]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: avoid futile mark worker acquisition
authorRhys Hiltner <rhys.hiltner@gmail.com>
Thu, 1 Aug 2024 18:15:30 +0000 (11:15 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 1 Aug 2024 21:11:15 +0000 (21:11 +0000)
During the GC mark phase, one of the first behaviors of findRunnable is
to check if it should execute a GC mark worker. Mark workers often run
for many milliseconds in a row, so programs that invoke the scheduler
more frequently will see that condition trigger only a tiny fraction of
the time.

Obtaining a mark worker from the gcBgMarkWorkerPool involves a CAS on a
single memory location that's shared across the process. When GOMAXPROCS
is large, the resulting contention can waste a significant amount of CPU
time. But a sufficiently large GOMAXPROCS also means there's no need for
fractional mark workers, making it easier to check ahead of time if we
need to run a worker.

Check, without committing to a particular worker, whether we would even
want to run one.

For #68399

Change-Id: I5d8578c2101ee20a8a4156a029584356095ea118
Reviewed-on: https://go-review.googlesource.com/c/go/+/602477
Reviewed-by: Michael Pratt <mpratt@google.com>
Auto-Submit: Rhys Hiltner <rhys.hiltner@gmail.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/runtime/mgcpacer.go

index cda87fe94844838bea217d6d99ca1bd776877282..3e80fae4f534647bdc9dee3ac8adbcd8d536c272 100644 (file)
@@ -752,6 +752,17 @@ func (c *gcControllerState) findRunnableGCWorker(pp *p, now int64) (*g, int64) {
                return nil, now
        }
 
+       if c.dedicatedMarkWorkersNeeded.Load() <= 0 && c.fractionalUtilizationGoal == 0 {
+               // No current need for dedicated workers, and no need at all for
+               // fractional workers. Check before trying to acquire a worker; when
+               // GOMAXPROCS is large, that can be expensive and is often unnecessary.
+               //
+               // When a dedicated worker stops running, the gcBgMarkWorker loop notes
+               // the need for the worker before returning it to the pool. If we don't
+               // see the need now, we wouldn't have found it in the pool anyway.
+               return nil, now
+       }
+
        // Grab a worker before we commit to running below.
        node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop())
        if node == nil {