From 7a8f39fa14d519f6efc34b2a783098bd107d17e0 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 7 Jul 2017 12:03:22 -0400 Subject: [PATCH] runtime: delay before osRelaxing Currently, sysmon relaxes the Windows timer resolution as soon as the Go process becomes idle. However, if it's going idle because of a short sleep (< 15.6 ms), this can turn that short sleep into a long sleep (15.6 ms). To address this, wait for 60 ms of idleness before relaxing the timer resolution. It would be better to check the time until the next wakeup and relax immediately if it makes sense, but there's currently no interaction between sysmon and the timer subsystem, so adding this simple delay is a much simpler and safer change for late in the release cycle. Fixes #20937. Change-Id: I817db24c3bdfa06dba04b7bc197cfd554363c379 Reviewed-on: https://go-review.googlesource.com/47832 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/os_windows.go | 6 ++++++ src/runtime/proc.go | 21 ++++++++++++++++++--- src/runtime/relax_stub.go | 6 ++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 72b57ad7dc..dcb232a995 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -270,6 +270,12 @@ var useLoadLibraryEx bool var timeBeginPeriodRetValue uint32 +// osRelaxDelay indicates that sysmon should wait for 60 ms of +// idleness before osRelaxing. Since osRelaxing may reduce timer +// resolution to 15.6 ms, this keeps timer error under roughly 1 part +// in 4. +const osRelaxDelay = 60 * 1e6 + // osRelax is called by the scheduler when transitioning to and from // all Ps being idle. // diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 9f8729a19b..0219d2d77d 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -3808,9 +3808,24 @@ func sysmon() { if scavengelimit < forcegcperiod { maxsleep = scavengelimit / 2 } - osRelax(true) - notetsleep(&sched.sysmonnote, maxsleep) - osRelax(false) + if osRelaxDelay > 0 { + // Wait before osRelaxing in + // case something happens soon. + sleep1 := int64(osRelaxDelay) + if sleep1 > maxsleep { + sleep1 = maxsleep + } + if notetsleep(&sched.sysmonnote, sleep1) { + maxsleep = 0 + } else { + maxsleep -= sleep1 + } + } + if maxsleep > 0 { + osRelax(true) + notetsleep(&sched.sysmonnote, maxsleep) + osRelax(false) + } lock(&sched.lock) atomic.Store(&sched.sysmonwait, 0) noteclear(&sched.sysmonnote) diff --git a/src/runtime/relax_stub.go b/src/runtime/relax_stub.go index 78c32736d7..648788118e 100644 --- a/src/runtime/relax_stub.go +++ b/src/runtime/relax_stub.go @@ -6,6 +6,12 @@ package runtime +// osRelaxDelay is the number of nanoseconds of idleness to tolerate +// before performing an osRelax. Since osRelax may reduce the +// precision of timers, this should be enough larger than the relaxed +// timer precision to keep the timer error acceptable. +const osRelaxDelay = 0 + // osRelax is called by the scheduler when transitioning to and from // all Ps being idle. func osRelax(relax bool) {} -- 2.50.0