diff options
author | qmuntal <quimmuntal@gmail.com> | 2023-04-25 18:27:35 +0200 |
---|---|---|
committer | Quim Muntal <quimmuntal@gmail.com> | 2024-02-19 15:44:49 +0000 |
commit | cf52e709977d331a70df9463cf9e307024b6779f (patch) | |
tree | e435ff9305d6bdf1d6ea8d998d752ece089f298e /src/time | |
parent | 5c92f43c51e9313504ff86bec9b0cd0e5eb1c1bc (diff) | |
download | go-cf52e709977d331a70df9463cf9e307024b6779f.tar.gz go-cf52e709977d331a70df9463cf9e307024b6779f.zip |
runtime: use a high res timer to signal io completion ports on windows
GetQueuedCompletionStatusEx has a ~16ms timeout resolution. Use a
WaitCompletionPacket associated with the I/O Completion Port (IOCP)
and a high resolution timer so the IOCP is signaled on timer expiry,
therefore improving the GetQueuedCompletionStatusEx timeout resolution.
BenchmarkSleep from the time package shows an important improvement:
goos: windows
goarch: amd64
pkg: time
cpu: Intel(R) Core(TM) i7-10850H CPU @ 2.70GHz
│ old.txt │ new.txt │
│ sec/op │ sec/op vs base │
Sleep-12 1258.5µ ± 5% 250.7µ ± 1% -80.08% (p=0.000 n=20)
Fixes #44343.
Change-Id: I79fc09e34dddfc49e0e23c3d1d0603926c22a11d
Reviewed-on: https://go-review.googlesource.com/c/go/+/488675
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Run-TryBot: Quim Muntal <quimmuntal@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/time')
-rw-r--r-- | src/time/sleep_test.go | 32 | ||||
-rw-r--r-- | src/time/time.go | 5 |
2 files changed, 25 insertions, 12 deletions
diff --git a/src/time/sleep_test.go b/src/time/sleep_test.go index b25606dfed..1ebc2d3c6a 100644 --- a/src/time/sleep_test.go +++ b/src/time/sleep_test.go @@ -15,15 +15,33 @@ import ( "sync/atomic" "testing" . "time" + _ "unsafe" // for go:linkname ) +// haveHighResSleep is true if the system supports at least ~1ms sleeps. +// +//go:linkname haveHighResSleep runtime.haveHighResSleep +var haveHighResSleep bool + +// adjustDelay returns an adjusted delay based on the system sleep resolution. // Go runtime uses different Windows timers for time.Now and sleeping. // These can tick at different frequencies and can arrive out of sync. // The effect can be seen, for example, as time.Sleep(100ms) is actually // shorter then 100ms when measured as difference between time.Now before and // after time.Sleep call. This was observed on Windows XP SP3 (windows/386). -// windowsInaccuracy is to ignore such errors. -const windowsInaccuracy = 17 * Millisecond +func adjustDelay(t *testing.T, delay Duration) Duration { + if haveHighResSleep { + return delay + } + t.Log("adjusting delay for low resolution sleep") + switch runtime.GOOS { + case "windows": + return delay - 17*Millisecond + default: + t.Fatal("adjustDelay unimplemented on " + runtime.GOOS) + return 0 + } +} func TestSleep(t *testing.T) { const delay = 100 * Millisecond @@ -33,10 +51,7 @@ func TestSleep(t *testing.T) { }() start := Now() Sleep(delay) - delayadj := delay - if runtime.GOOS == "windows" { - delayadj -= windowsInaccuracy - } + delayadj := adjustDelay(t, delay) duration := Now().Sub(start) if duration < delayadj { t.Fatalf("Sleep(%s) slept for only %s", delay, duration) @@ -247,10 +262,7 @@ func TestAfter(t *testing.T) { const delay = 100 * Millisecond start := Now() end := <-After(delay) - delayadj := delay - if runtime.GOOS == "windows" { - delayadj -= windowsInaccuracy - } + delayadj := adjustDelay(t, delay) if duration := Now().Sub(start); duration < delayadj { t.Fatalf("After(%s) slept for only %d ns", delay, duration) } diff --git a/src/time/time.go b/src/time/time.go index 9d4c6e919e..2ca1cdbb72 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -81,8 +81,9 @@ // // Timer resolution varies depending on the Go runtime, the operating system // and the underlying hardware. -// On Unix, the resolution is approximately 1ms. -// On Windows, the default resolution is approximately 16ms, but +// On Unix, the resolution is ~1ms. +// On Windows version 1803 and newer, the resolution is ~0.5ms. +// On older Windows versions, the default resolution is ~16ms, but // a higher resolution may be requested using [golang.org/x/sys/windows.TimeBeginPeriod]. package time |