]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: usleep before stealing runnext only if not in syscall
authorMichael Anthony Knyszek <mknyszek@google.com>
Fri, 14 Nov 2025 17:41:58 +0000 (17:41 +0000)
committerMichael Knyszek <mknyszek@google.com>
Fri, 14 Nov 2025 23:13:00 +0000 (15:13 -0800)
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>
src/runtime/proc.go

index 44dbb2fd44b7a642a01c9ec2f6fd9cd6542b3328..61364d91ff90660b8472b4bb64541b83a4c70f2a 100644 (file)
@@ -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) {