mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
This commit is contained in:
parent
410ef44f00
commit
d55ecea9e5
1 changed files with 30 additions and 17 deletions
|
|
@ -7507,23 +7507,36 @@ func runqgrab(pp *p, batch *[256]guintptr, batchHead uint32, stealRunNextG bool)
|
||||||
// Try to steal from pp.runnext.
|
// Try to steal from pp.runnext.
|
||||||
if next := pp.runnext; next != 0 {
|
if next := pp.runnext; next != 0 {
|
||||||
if pp.status == _Prunning {
|
if pp.status == _Prunning {
|
||||||
// Sleep to ensure that pp isn't about to run the g
|
if mp := pp.m.ptr(); mp != nil {
|
||||||
// we are about to steal.
|
if gp := mp.curg; gp == nil || readgstatus(gp)&^_Gscan != _Gsyscall {
|
||||||
// The important use case here is when the g running
|
// Sleep to ensure that pp isn't about to run the g
|
||||||
// on pp ready()s another g and then almost
|
// we are about to steal.
|
||||||
// immediately blocks. Instead of stealing runnext
|
// The important use case here is when the g running
|
||||||
// in this window, back off to give pp a chance to
|
// on pp ready()s another g and then almost
|
||||||
// schedule runnext. This will avoid thrashing gs
|
// immediately blocks. Instead of stealing runnext
|
||||||
// between different Ps.
|
// in this window, back off to give pp a chance to
|
||||||
// A sync chan send/recv takes ~50ns as of time of
|
// schedule runnext. This will avoid thrashing gs
|
||||||
// writing, so 3us gives ~50x overshoot.
|
// between different Ps.
|
||||||
if !osHasLowResTimer {
|
// A sync chan send/recv takes ~50ns as of time of
|
||||||
usleep(3)
|
// writing, so 3us gives ~50x overshoot.
|
||||||
} else {
|
// If curg is nil, we assume that the P is likely
|
||||||
// On some platforms system timer granularity is
|
// to be in the scheduler. If curg isn't nil and isn't
|
||||||
// 1-15ms, which is way too much for this
|
// in a syscall, then it's either running, waiting, or
|
||||||
// optimization. So just yield.
|
// runnable. In this case we want to sleep because the
|
||||||
osyield()
|
// 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) {
|
if !pp.runnext.cas(next, 0) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue