When using this loop{} construct and running my own program, I notice my laptop fans kicking into high gear. So, I'm wondering if there is a better way to consume the Gilrs events?
I don't have this type of issue when writing Node, or at least I've never had to use a loop{} construct to passively consume events, so I'm guessing I just have some kind of fundamental misunderstanding for Rust. My intuition is that such a simple program doesn't need to be the most CPU hungry thing on the machine, and I that I need to rate limit the loop in some way (as silly as that sounds).
I tried to search for similar topics about 100% CPU usage, but didn't find an answer; probably because this is a really basic question, and I'm just ignorant / missing something obvious?
If someone can help lift my ignorance here I would appreciate the help. I have a feeling the answer is going to be obvious but for whatever reason I can't think what to try next.
Okay, this makes sense. I feel uneasy about the ambiguity of throwing in a random amount of milliseconds to sleep in the middle of my code however. How do you know how many milliseconds to sleep? Does that change for each system with different CPU speeds?
Perhaps there is another way to do this that is more like consuming a JavaScript promise?
The timing doesn't change for different cpu speeds, in that it will never run faster than you specify but it may run slower.
Yes, you can use async functions in Rust, but somewhere deep down you have a loop like this driving everything, you just don't see it in some environments like Node (I have zero experience with Node, so I am basing that on your description).
Games tends to limit its update-per-second(UPS) in some arbitrary number, commonly 20 or 60 ups. For example in 20ups limit, each game loop has 50ms time budget and if it completes before 50ms it sleep() to pass the rest of time slots.
Your loop is doing exactly what you asked for. Running around, as fast as possible, forever, doing nothing. Of course the act of running around consumes all your CPU time.
Javascript has it's own "event loop" built into the language and run-time. So there is no need to code such a wait loop in JS. The run time is doing it for you.
Javascript is the only language I have come across that has such an even loop built in. Others, including Rust do not. Which means that if you run out off the end of main() then everything your program ever created is destroyed, everything stops and the program exits.
Hence you need to keep things "live" with a busy loop or by calling join() on threads and so on.
The gilrs example calls next_event inside the loop. This is a blocking call, so it will cause the thread to do nothing until a new event is available, which will prevent it from using 100% CPU.
If you are writing a game, your graphics library or game engine will probably have additional ways to sync your event loop to your input devices or output frame rate.
I'm going to have to look into this, first by trying the default Gilrs example, and then if that is not using 100%, see why my program that I based off the Gilrs example is not blocking. Thanks for explaining.
I tried the example from the Gilrs docs, and it is showing 100% like the screen shot in the OP.
Adding the following sleep makes the CPU usage more like 1%:
use std::{thread, time};
fn main() {
use gilrs::{Gilrs, Button, Event};
let mut gilrs = Gilrs::new().unwrap();
let ten_millis = time::Duration::from_millis(10);
// Iterate over all connected gamepads
for (_id, gamepad) in gilrs.gamepads() {
println!("{} is {:?}", gamepad.name(), gamepad.power_info());
}
let mut active_gamepad = None;
loop {
// Examine new events
while let Some(Event { id, event, time }) = gilrs.next_event() {
println!("{:?} New event from {}: {:?}", time, id, event);
active_gamepad = Some(id);
}
// You can also use cached gamepad state
if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) {
if gamepad.is_pressed(Button::South) {
println!("Button South is pressed (XBox - A, PS - X)");
}
}
thread::sleep(ten_millis);
}
}
So, @mbrubeck, is this library not blocking like you thought?
std::thread::yield_now doesn't always block the thread. What it does is to ask OS to run another thread first if exist. So your CPU will still be spinning in 100%, activating OS thread scheduler for every loop in this time.