I'm coding a simple piano roll app that uses the SDL2 bindings for Rust to do its event handling and rendering. I have something very similar to the following code:
let fps = 60;
let accumulator = 0; // Time in seconds
'running: loop {
let t0 = std::time::Instant::now();
poll_events();
if accumulator > 1.0 / fps {
update();
render();
counter -= 1.0 / fps;
}
let t1 = std::time::Instant::now();
let delta = (t1 - t0).as_secs_f64();
accumulator = delta;
// Busy wait?
}
Generally, the application is running fine and it doesn't have any noticeable artifacts, at least to my untrained eye. However, the CPU usage is through the roof, using nearly 25% in average (plus some GPU usage for the rendering).
I've checked the CPU usage of a very similar program which has many more features and also has better graphics, and when given the same MIDI notes to display, it averages at 2% CPU usage, plus 10% on the GPU side.
I also benchmarked my code, and found out the following approximated timings:
- poll_events() : ~0.002 ms
- update() : ~0.1 ms
- render() : ~1 ms
Given that I'm aiming for 60 fps at both the logic level and the rendering level, I have roughly 16 milliseconds to do a full poll/update/render cycle. At the moment I'm using about 2 milliseconds (being generous) of the full range, so my conclusion is that the main loop has some busy wait going on, which I want to get rid of.
I've tried mainly sleep-based solutions which are quite unreliable, since the sleeping time depends on the operating system and at least in my machine (Windows 11) it's around 10-20 milliseconds, causing noticeable delays in the animation.
From what I read there are some thread-related solutions to avoid this kind of situation, but I feel like it's a totally unnecessary area to get into since I'm way below the point of needing any concurrency to squeeze more performance out of the machine.
I've been learning Rust for a couple of weeks, and although I've used SDL2 before in a smaller project using C , I had the same problem and I couldn't find a suitable solution.
I'm not sure if this is a problem specifically related to SDL2 or if it also happens using other libraries, but any help would be very appreciated.
CodePudding user response:
As another post had already discussed some versions of Windows use a 15ms sleep time by default, but the OS does have a more precise sleep time which can be configured down to about 0.5ms.
There is a Rust crate that allows you to access a more precise timer by using the OS timer plus a little bit of busy wait for the remainders. That said, I didn't use this feature, as the native_sleep()
function already gives me the resolution I needed.
The updated code looks something like this:
let fps = 60.;
let accumulator = 0.; // Time in seconds
'running: loop {
let t0 = std::time::Instant::now();
poll_events();
if accumulator > 1.0 / fps {
update();
render();
counter -= 1.0 / fps;
}
// Fix
let sleep_time = std::time::Duration::from_millis(1);
spin_sleep::native_sleep(sleep_time);
let t1 = std::time::Instant::now();
let delta = (t1 - t0).as_secs_f64();
accumulator = delta;
}