Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Higher-precision sleep on Windows #43376

Closed
clarfonthey opened this issue Jul 21, 2017 · 10 comments · Fixed by #116461
Closed

Higher-precision sleep on Windows #43376

clarfonthey opened this issue Jul 21, 2017 · 10 comments · Fixed by #116461
Labels
A-thread Area: `std::thread` C-feature-accepted Category: A feature request that has been accepted pending implementation. E-help-wanted Call for participation: Help is requested to fix this issue. O-windows Operating system: Windows T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@clarfonthey
Copy link
Contributor

Right now, the sleep method uses Sleep on Windows, which only has millisecond precision. Even though the function explicitly allows rounding up to higher ticks, it would be nice to allow higher-precision timers.

After a quick search I was able to find that the SetWaitableTimer function (used in this gist) supports much higher precision timing, so, this is theoretically possible. I'm not particularly good at Windows programming, though, so, I shouldn't be doing this myself.

@jrmuizel
Copy link
Contributor

I don't believe this will actually let you sleep for periods less than 1ms. See: https://stackoverflow.com/questions/7827062/is-there-a-windows-equivalent-of-nanosleep?rq=1 and https://stackoverflow.com/questions/85122/how-to-make-thread-sleep-less-than-a-millisecond-on-windows

@Mark-Simulacrum Mark-Simulacrum added C-bug Category: This is a bug. C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed C-bug Category: This is a bug. labels Jul 26, 2017
@dtolnay dtolnay added C-feature-accepted Category: A feature request that has been accepted pending implementation. and removed C-feature-request Category: A feature request, i.e: not implemented / a PR. labels Nov 18, 2017
@dtolnay
Copy link
Member

dtolnay commented Nov 18, 2017

This does not affect the API of std::thread::sleep at all right, just the implementation on Windows? If so, I would welcome a PR!

@Mark-Simulacrum Mark-Simulacrum added the O-windows Operating system: Windows label Sep 2, 2019
@m-ou-se m-ou-se added E-help-wanted Call for participation: Help is requested to fix this issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. and removed T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Oct 21, 2021
@haesy
Copy link

haesy commented Nov 6, 2021

On my current Windows 10 machine, std::thread::sleep has a minimum sleep time of roughly 15 ms. Calling timeBeginPeriod(1) at the start of the application sets the timer resolution to the lowest possible value (for this API) of 1 ms. After that I get minimum sleep times of roughly 1.8 ms.

I also translated the gist to Rust using the windows-rs crate and got even lower sleep times, but the results were quite unreliable. A sleep time of 1 ms sleeps between 0.8 ms (violates the rules of std::thread::sleep) and 1.8 ms. With this method the lowest sleep time is round about 0.8 ms. I had to use timeBeginPeriod as well, otherwise I still got a 15 ms resolution.

The first solution using timeBeginPeriod seems to be an easy way to get a better resolution. However, this is a system/process wide setting, depending on the version of Windows. I don't know if this has unintended consequences for other libraries/CPU usage and if we need to call timeEndPeriod at application shutdown or if Windows handles that automatically.

The second solution is much more complicated, because it does not guarantee a minimum sleep time. This means we would need additional code that checks for that.

Great resource for further reading: https://randomascii.wordpress.com/2020/10/04/windows-timer-resolution-the-great-rule-change/

@joshtriplett
Copy link
Member

cc @ChrisDenton for recommendations here.

@ChrisDenton
Copy link
Member

I'm not entirely convinced we should be setting timeBeginPeriod for the application. Or even if we can do so cleanly in libraries. We do have to play well with others. So this feels like something that should be opted in to, especially as it affects the system (to a greater or lesser degree depending on the Windows version). It's also unclear to me if the added complexity of waitable timers will be worth it once you adjust for early wakes, but maybe it is.

To be clear, I'm not totally against it in principle. I'm just uncertain if this is practical for the standard library.

@joshtriplett
Copy link
Member

@ChrisDenton I agree that we shouldn't call timeBeginPeriod internally.

@ChrisDenton
Copy link
Member

Ok so the question is if using SetWaitableTimer may be useful for sub millisecond timers. However, as the "Remarks" section of the docs say, it's precision is controlled by timeBeginPeriod which takes a time in milliseconds. So millisecond precision is all that's guaranteed (and even that's subject to the whims of the hardware and thread scheduler).

So I don't feel like this is a truly higher precision sleep, even if it sometimes wakes early. I would however revise my opinion if there were some projects that can show switching from sleep to waitable timers helped in some real way but I don't feel like this is worth it otherwise.

@haesy
Copy link

haesy commented Jan 30, 2022

Go uses SetWaitableTimer in their internal usleep function on Windows, but I don't know why. I still only get ~15.6ms accuracy in Go on my Windows machines. Maybe it's useful for loops that regularly check if there is something to do and sleep otherwise (Go runtime, Tokio, game loops, ...)?

If SetWaitableTimer depends fully on timeBeginPeriod, I fail to see any advantages, because the only thing we would get is the possibility to wake up early, which is not desired for Sleep. So the question is not sleep vs waitable timers, but if and how timeBeginPeriod can/should be used in the standard library:

The advantage would be a slightly better accuracy up to ~1ms (from ~15.6ms). That's good enough to make it viable for e.g. games with 60 fps. To use it kind of safely we would need an additional function that the user has to call explicitly (opt-in e.g. in fn main) and calls timeEndPeriod at the end of the scope via Drop. Unless other platforms support something similar, I don't think the standard library is the right place for that. An external platform specific crate is probably a better solution.

Maybe the documentation can be updated to reflect the currently known limits on Windows.

@the8472 the8472 added A-time Area: Time A-thread Area: `std::thread` and removed A-time Area: Time labels Feb 8, 2022
@ChrisDenton
Copy link
Member

ChrisDenton commented Apr 19, 2022

Hm, I've been looking in to the CREATE_WAITABLE_TIMER_HIGH_RESOLUTION flag some more. It seems on my system to round up to the nearest ~500 microseconds (well, a bit over) but this depends on a hardware interrupt so it may vary between systems. Edit: actually it can be a lot better than that but it's highly variable.

The flag still has yet to be documented (a recurring theme for newer changes) but it's in public headers and has been around for years at this point.

I think it would be great if anyone wants to make a PR that creates a waitable timer using this flag, at the very least for testing purposes. Though it would need a fallback for if CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is not recognized.

@haesy
Copy link

haesy commented Apr 20, 2022

CREATE_WAITABLE_TIMER_HIGH_RESOLUTION seems to be independent of timeBeginPeriod. Doesn't seem to wake up early either. Python now use it as well. Looks like a good solution.

I never contributed to Rust and would first need some time to figure that out (and find some spare time). If someone is faster or wants to test it out independently, I shared my example project with some comments/links to documentation here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-thread Area: `std::thread` C-feature-accepted Category: A feature request that has been accepted pending implementation. E-help-wanted Call for participation: Help is requested to fix this issue. O-windows Operating system: Windows T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants