aboutsummaryrefslogtreecommitdiff
path: root/src/time
diff options
context:
space:
mode:
authorqmuntal <quimmuntal@gmail.com>2023-04-25 18:27:35 +0200
committerQuim Muntal <quimmuntal@gmail.com>2024-02-19 15:44:49 +0000
commitcf52e709977d331a70df9463cf9e307024b6779f (patch)
treee435ff9305d6bdf1d6ea8d998d752ece089f298e /src/time
parent5c92f43c51e9313504ff86bec9b0cd0e5eb1c1bc (diff)
downloadgo-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.go32
-rw-r--r--src/time/time.go5
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