Skip to content

Commit d55ecea

Browse files
committed
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 <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent 410ef44 commit d55ecea

File tree

1 file changed

+30
-17
lines changed

1 file changed

+30
-17
lines changed

src/runtime/proc.go

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7507,23 +7507,36 @@ func runqgrab(pp *p, batch *[256]guintptr, batchHead uint32, stealRunNextG bool)
75077507
// Try to steal from pp.runnext.
75087508
if next := pp.runnext; next != 0 {
75097509
if pp.status == _Prunning {
7510-
// Sleep to ensure that pp isn't about to run the g
7511-
// we are about to steal.
7512-
// The important use case here is when the g running
7513-
// on pp ready()s another g and then almost
7514-
// immediately blocks. Instead of stealing runnext
7515-
// in this window, back off to give pp a chance to
7516-
// schedule runnext. This will avoid thrashing gs
7517-
// between different Ps.
7518-
// A sync chan send/recv takes ~50ns as of time of
7519-
// writing, so 3us gives ~50x overshoot.
7520-
if !osHasLowResTimer {
7521-
usleep(3)
7522-
} else {
7523-
// On some platforms system timer granularity is
7524-
// 1-15ms, which is way too much for this
7525-
// optimization. So just yield.
7526-
osyield()
7510+
if mp := pp.m.ptr(); mp != nil {
7511+
if gp := mp.curg; gp == nil || readgstatus(gp)&^_Gscan != _Gsyscall {
7512+
// Sleep to ensure that pp isn't about to run the g
7513+
// we are about to steal.
7514+
// The important use case here is when the g running
7515+
// on pp ready()s another g and then almost
7516+
// immediately blocks. Instead of stealing runnext
7517+
// in this window, back off to give pp a chance to
7518+
// schedule runnext. This will avoid thrashing gs
7519+
// between different Ps.
7520+
// A sync chan send/recv takes ~50ns as of time of
7521+
// writing, so 3us gives ~50x overshoot.
7522+
// If curg is nil, we assume that the P is likely
7523+
// to be in the scheduler. If curg isn't nil and isn't
7524+
// in a syscall, then it's either running, waiting, or
7525+
// runnable. In this case we want to sleep because the
7526+
// P might either call into the scheduler soon (running),
7527+
// or already is (since we found a waiting or runnable
7528+
// goroutine hanging off of a running P, suggesting it
7529+
// either recently transitioned out of running, or will
7530+
// transition to running shortly).
7531+
if !osHasLowResTimer {
7532+
usleep(3)
7533+
} else {
7534+
// On some platforms system timer granularity is
7535+
// 1-15ms, which is way too much for this
7536+
// optimization. So just yield.
7537+
osyield()
7538+
}
7539+
}
75277540
}
75287541
}
75297542
if !pp.runnext.cas(next, 0) {

0 commit comments

Comments
 (0)