swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.27k stars 10.33k forks source link

Task.sleep() is inaccurate on Windows #64935

Open MarSe32m opened 1 year ago

MarSe32m commented 1 year ago

Description Sleeping a task on Windows for relatively small time intervals, e.g. < 10ms, is inaccruate even when one uses timeBeginPeriod().

Steps to reproduce The following code

import WinSDK

@main
public struct App {
    public static func main() async throws {
        timeBeginPeriod(1)
        for _ in 0..<100 {
            let sleepTime = try await ContinuousClock().measure {
                try await Task.sleep(for: .milliseconds(1), tolerance: .nanoseconds(0))
            }
            print(sleepTime)
        }
        timeEndPeriod(1)
    }
}

prints

.
.
.
0.0318463 seconds
0.0317875 seconds
0.0313313 seconds
0.0318428 seconds
0.0318439 seconds
0.0312897 seconds
0.0318454 seconds
0.0318362 seconds
0.031353 seconds
0.0318502 seconds
0.0308014 seconds
0.0313329 seconds
0.031854 seconds

Expected behavior It should print something like 0.001.. seconds.

Using Thread.sleep() from Foundation gives the expected result.

import Foundation
import WinSDK

@main
public struct App {
    public static func main() async throws {
        timeBeginPeriod(1)
        for _ in 0..<100 {
            let sleepTime = ContinuousClock().measure {
                Thread.sleep(forTimeInterval: 0.001)
            }
            print(sleepTime)
        }
        timeEndPeriod(1)
    }
}
.
.
.
0.0018896 seconds
0.0009295 seconds
0.0009292 seconds
0.0019314 seconds
0.0009375 seconds
0.0009266 seconds
0.0019474 seconds
0.0009317 seconds
0.0009317 seconds
0.000954 seconds
0.0009257 seconds
0.0019632 seconds

Environment

compnerd commented 1 year ago

This is very difficult to support. IIRC, the default time quanta on Windows is 10ms (or was it 15ms?). In theory you can do some things that get you a higher resolution (at least in the kernel). However, that will significantly impact battery life. This might require an evolution proposal to explicitly opt-in to a power impacting higher resolution timer beyond the system default.

grynspan commented 9 months ago

It's also worth noting that {Suspending,Continuous}Clock.measure {} has coarser granularity on Windows than on e.g. Darwin.