Rust lang thread::sleep() sleeping for almost twice the specified time during game loop on windows
Asked Answered
L

1

7

So I've written the following function to show what i mean:

use std::{thread, time};

const TARGET_FPS: u64 = 60;

fn main() {
    let mut frames = 0;
    let target_ft = time::Duration::from_micros(1000000 / TARGET_FPS);
    println!("target frame time: {:?}",target_ft);
    let mut time_slept = time::Duration::from_micros(0);
    let start = time::Instant::now();
    loop {
        let frame_time = time::Instant::now();

        frames+=1;
        if frames == 60 {
            break
        }

        if let Some(i) = (target_ft).checked_sub(frame_time.elapsed()) {
            time_slept+=i;
            thread::sleep(i)
        }
    }
    println!("time elapsed: {:?}",start.elapsed());
    println!("time slept: {:?}",time_slept);
}

The idea of the function is to execute 60 cycles at 60fps then exit with the time elapsed and the total time spent sleeping during the loop. ideally, since im executing 60 cycles at 60fps with no real calculations happening between, it should take about one second to execute and spend basically the entire second sleeping. but instead when i run it it returns:

target frame time: 16.666ms
time elapsed: 1.8262798s
time slept: 983.2533ms

As you can see, even though it was only told to sleep for a total of 983ms, the 60 cycles took nearly 2 seconds to complete. Because of this nearly 50% inaccuracy, a loop told to run at 60fps instead runs at only 34fps.

The docs say The thread may sleep longer than the duration specified due to scheduling specifics or platform-dependent functionality. It will never sleep less. But is this really just from that? Am i doing something wrong?

Legg answered 27/7, 2020 at 8:6 Comment(2)
I don't know why it happens on your machine, but running the code on my Linux PC gives me correct results: target frame time: 16.666ms time elapsed: 998.832361ms time slept: 983.268846msBuie
Usually to implement such a thing you have to do something way more complex than your solution. What I would do is to measure current time after I sleep, figure out how much I should sleep next and sleep that amount of time, so I could keep the FPS somewhat constant. With your solution, if drawing the actual frame takes a longer time, you would run out of time frames anyway.Bayer
L
6

i switched to using spin_sleep::sleep(i) from https://crates.io/crates/spin_sleep and it seems to have fixed it. i guess it must just be windows inaccuracies then...still strange that time::sleep on windows would be that far off for something as simple as a game loop

Legg answered 27/7, 2020 at 8:36 Comment(2)
> The thread may sleep longer than the duration specified due to scheduling specifics or platform-dependent functionality. It will never sleep less.Radiance
And Windows is well-known for having rather low sleep accuracy, it has (had) a default timer resolution of ~16ms so that's the lowest it would sleep. Funnily enough the resolution can be configured down to sub-ms and running some software drastically improves sleep accuracy as they configure an ms-precision resolution, and it's a system-wide thing.Zingale

© 2022 - 2024 — McMap. All rights reserved.