From 11eaf428867417b9d5fab4deadd0ef03c9fd9773 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 21 Mar 2017 16:45:12 -0400 Subject: [PATCH] runtime: reduce Windows timer resolution when idle Currently Go sets the system-wide timer resolution to 1ms the whole time it's running. This has negative affects on system performance and power consumption. Unfortunately, simply reducing the timer resolution to the default 15ms interferes with several sleeps in the runtime itself, including sysmon's ability to interrupt goroutines. This commit takes a hybrid approach: it only reduces the timer resolution when the Go process is entirely idle. When the process is idle, nothing needs a high resolution timer. When the process is non-idle, it's already consuming CPU so it doesn't really matter if the OS also takes timer interrupts more frequently. Updates #8687. Change-Id: I0652564b4a36d61a80e045040094a39c19da3b06 Reviewed-on: https://go-review.googlesource.com/38403 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman Reviewed-by: Dmitry Vyukov --- src/runtime/os_windows.go | 19 ++++++++++++++++++- src/runtime/proc.go | 2 ++ src/runtime/relax_stub.go | 11 +++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/runtime/relax_stub.go diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 672cc100d5cf4c..3df3d28ed08e7a 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -50,6 +50,7 @@ const ( //go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll" //go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll" //go:cgo_import_dynamic runtime._timeBeginPeriod timeBeginPeriod%1 "winmm.dll" +//go:cgo_import_dynamic runtime._timeEndPeriod timeEndPeriod%1 "winmm.dll" type stdFunction unsafe.Pointer @@ -96,6 +97,7 @@ var ( _WriteConsoleW, _WriteFile, _timeBeginPeriod, + _timeEndPeriod, _ stdFunction // Following syscalls are only available on some Windows PCs. @@ -268,6 +270,21 @@ var useLoadLibraryEx bool var timeBeginPeriodRetValue uint32 +// osRelax is called by the scheduler when transitioning to and from +// all Ps being idle. +// +// On Windows, it adjusts the system-wide timer resolution. Go needs a +// high resolution timer while running and there's little extra cost +// if we're already using the CPU, but if all Ps are idle there's no +// need to consume extra power to drive the high-res timer. +func osRelax(relax bool) uint32 { + if relax { + return uint32(stdcall1(_timeEndPeriod, 1)) + } else { + return uint32(stdcall1(_timeBeginPeriod, 1)) + } +} + func osinit() { asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall)) usleep2Addr = unsafe.Pointer(funcPC(usleep2)) @@ -287,7 +304,7 @@ func osinit() { stdcall2(_SetConsoleCtrlHandler, funcPC(ctrlhandler), 1) - timeBeginPeriodRetValue = uint32(stdcall1(_timeBeginPeriod, 1)) + timeBeginPeriodRetValue = osRelax(false) ncpu = getproccount() diff --git a/src/runtime/proc.go b/src/runtime/proc.go index dae8f135bcd904..7d6b89016a6052 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -3751,7 +3751,9 @@ func sysmon() { if scavengelimit < forcegcperiod { maxsleep = scavengelimit / 2 } + 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 new file mode 100644 index 00000000000000..78c32736d76d3e --- /dev/null +++ b/src/runtime/relax_stub.go @@ -0,0 +1,11 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows + +package runtime + +// osRelax is called by the scheduler when transitioning to and from +// all Ps being idle. +func osRelax(relax bool) {}