aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/proc.go
diff options
context:
space:
mode:
authorMichael Anthony Knyszek <mknyszek@google.com>2025-11-14 17:41:58 +0000
committerMichael Knyszek <mknyszek@google.com>2025-11-14 15:13:00 -0800
commitd55ecea9e5a5a4cfba30c6f35d4841ae66e05ccd (patch)
treeb4c0d3dba5c11671d8d0958901871e523885fde9 /src/runtime/proc.go
parent410ef44f0054a9ab20a901895edb7db5a4d0aad7 (diff)
downloadgo-d55ecea9e5a5a4cfba30c6f35d4841ae66e05ccd.tar.xz
runtime: usleep before stealing runnext only if not in syscall
In the scheduler's steal path, we usleep(3) before stealing a _Prunning P's runnext slot. Before CL 646198, we would not call usleep(3) if the P was in _Psyscall. After CL 646198, Ps with Gs in syscalls stay in _Prunning until stolen, meaning we might unnecessarily usleep(3) where we didn't before. This probably isn't a huge deal in most cases, but can cause some apparent slowdowns in microbenchmarks that frequently take the steal path while there are syscalling goroutines. Change-Id: I5bf3df10fe61cf8d7f0e9fe9522102de66faf344 Reviewed-on: https://go-review.googlesource.com/c/go/+/720441 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/runtime/proc.go')
-rw-r--r--src/runtime/proc.go47
1 files changed, 30 insertions, 17 deletions
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 44dbb2fd44..61364d91ff 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -7507,23 +7507,36 @@ func runqgrab(pp *p, batch *[256]guintptr, batchHead uint32, stealRunNextG bool)
// Try to steal from pp.runnext.
if next := pp.runnext; next != 0 {
if pp.status == _Prunning {
- // Sleep to ensure that pp isn't about to run the g
- // we are about to steal.
- // The important use case here is when the g running
- // on pp ready()s another g and then almost
- // immediately blocks. Instead of stealing runnext
- // in this window, back off to give pp a chance to
- // schedule runnext. This will avoid thrashing gs
- // between different Ps.
- // A sync chan send/recv takes ~50ns as of time of
- // writing, so 3us gives ~50x overshoot.
- if !osHasLowResTimer {
- usleep(3)
- } else {
- // On some platforms system timer granularity is
- // 1-15ms, which is way too much for this
- // optimization. So just yield.
- osyield()
+ if mp := pp.m.ptr(); mp != nil {
+ if gp := mp.curg; gp == nil || readgstatus(gp)&^_Gscan != _Gsyscall {
+ // Sleep to ensure that pp isn't about to run the g
+ // we are about to steal.
+ // The important use case here is when the g running
+ // on pp ready()s another g and then almost
+ // immediately blocks. Instead of stealing runnext
+ // in this window, back off to give pp a chance to
+ // schedule runnext. This will avoid thrashing gs
+ // between different Ps.
+ // A sync chan send/recv takes ~50ns as of time of
+ // writing, so 3us gives ~50x overshoot.
+ // If curg is nil, we assume that the P is likely
+ // to be in the scheduler. If curg isn't nil and isn't
+ // in a syscall, then it's either running, waiting, or
+ // runnable. In this case we want to sleep because the
+ // P might either call into the scheduler soon (running),
+ // or already is (since we found a waiting or runnable
+ // goroutine hanging off of a running P, suggesting it
+ // either recently transitioned out of running, or will
+ // transition to running shortly).
+ if !osHasLowResTimer {
+ usleep(3)
+ } else {
+ // On some platforms system timer granularity is
+ // 1-15ms, which is way too much for this
+ // optimization. So just yield.
+ osyield()
+ }
+ }
}
}
if !pp.runnext.cas(next, 0) {