diff options
| author | Ian Lance Taylor <iant@golang.org> | 2020-01-13 12:17:26 -0800 |
|---|---|---|
| committer | Ian Lance Taylor <iant@golang.org> | 2020-01-14 19:54:20 +0000 |
| commit | cfe3cd903f018dec3cb5997d53b1744df4e53909 (patch) | |
| tree | e685f5fa8ca0df1bd74b2c2a634a3b70dc276deb /src/runtime/proc.go | |
| parent | 71154e061f067a668e7b619d7b3701470b8561be (diff) | |
| download | go-cfe3cd903f018dec3cb5997d53b1744df4e53909.tar.xz | |
runtime: keep P's first timer when in new atomically accessed field
This reduces lock contention when only a few P's are running and
checking for whether they need to run timers on the sleeping P's.
Without this change the running P's would get lock contention
while looking at the sleeping P's timers. With this change a single
atomic load suffices to determine whether there are any ready timers.
Change-Id: Ie843782bd56df49867a01ecf19c47498ec827452
Reviewed-on: https://go-review.googlesource.com/c/go/+/214185
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: David Chase <drchase@google.com>
Diffstat (limited to 'src/runtime/proc.go')
| -rw-r--r-- | src/runtime/proc.go | 53 |
1 files changed, 36 insertions, 17 deletions
diff --git a/src/runtime/proc.go b/src/runtime/proc.go index a7eb05fcba..9e2833fe6a 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -2621,6 +2621,21 @@ func dropg() { // We pass now in and out to avoid extra calls of nanotime. //go:yeswritebarrierrec func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) { + // If there are no timers to adjust, and the first timer on + // the heap is not yet ready to run, then there is nothing to do. + if atomic.Load(&pp.adjustTimers) == 0 { + next := int64(atomic.Load64(&pp.timer0When)) + if next == 0 { + return now, 0, false + } + if now == 0 { + now = nanotime() + } + if now < next { + return now, next, false + } + } + lock(&pp.timersLock) adjusttimers(pp) @@ -4095,6 +4110,7 @@ func (pp *p) destroy() { pp.timers = nil pp.adjustTimers = 0 pp.deletedTimers = 0 + atomic.Store64(&pp.timer0When, 0) unlock(&pp.timersLock) unlock(&plocal.timersLock) } @@ -4421,23 +4437,26 @@ func checkdead() { } // Maybe jump time forward for playground. - _p_ := timejump() - if _p_ != nil { - for pp := &sched.pidle; *pp != 0; pp = &(*pp).ptr().link { - if (*pp).ptr() == _p_ { - *pp = _p_.link - break + if faketime != 0 { + when, _p_ := timeSleepUntil() + if _p_ != nil { + faketime = when + for pp := &sched.pidle; *pp != 0; pp = &(*pp).ptr().link { + if (*pp).ptr() == _p_ { + *pp = _p_.link + break + } } + mp := mget() + if mp == nil { + // There should always be a free M since + // nothing is running. + throw("checkdead: no m for timer") + } + mp.nextp.set(_p_) + notewakeup(&mp.park) + return } - mp := mget() - if mp == nil { - // There should always be a free M since - // nothing is running. - throw("checkdead: no m for timer") - } - mp.nextp.set(_p_) - notewakeup(&mp.park) - return } // There are no goroutines running, so we can look at the P's. @@ -4482,7 +4501,7 @@ func sysmon() { } usleep(delay) now := nanotime() - next := timeSleepUntil() + next, _ := timeSleepUntil() if debug.schedtrace <= 0 && (sched.gcwaiting != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs)) { lock(&sched.lock) if atomic.Load(&sched.gcwaiting) != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs) { @@ -4504,7 +4523,7 @@ func sysmon() { osRelax(false) } now = nanotime() - next = timeSleepUntil() + next, _ = timeSleepUntil() lock(&sched.lock) atomic.Store(&sched.sysmonwait, 0) noteclear(&sched.sysmonnote) |
