I'm working on a program that updates the background of an x11 desktop at a specified interval. However, it eats large amounts of my CPU just sitting idle. I know this is due to the main loop running faster than I want it to. I've used a couple shameful hacks to quell usage down to about 15% but now I am searching for a long-term solution.
Here is the function that contains the main loop:
pub fn run_mapped(&mut self) {
info!(self.logger, "Running in mapped mode!");
let wait_d = Duration::new(0,500);
let long_wait_d = Duration::new(0,750);
loop {
if self.img_dir.1.elapsed() > self.reload_time {
self.img_dir.0.reload();
self.img_dir.1 = Instant::now();
}
let cd = self.x.get_current_desktop();
// change background if timeout is reached
if self.since_timeout.elapsed() > self.timeout {
self.change_backgrounds();
let ref mut current_bg = self.image_map[cd];
self.since_timeout = Instant::now();
self.x.change_background(current_bg);
}
match self.x.next_event() {
Some(_) => {
let ref mut current_bg = self.image_map[cd];
self.x.change_background(current_bg);
},
None => { sleep(wait_d); }
}
sleep(long_wait_d);
}
}
Not shown, but I have additionally set process priority lower to give more important process priority over this one.
I added the sleep calls to prevent>50% CPU usage but it still consumes ~20% which seems a little excessive for what I want this program to do. I'm looking at some of the CPU usage from other programs I'm running in my DE (candybar) and they're nowhere near as resource-hungry as my program.
All that being said, my two questions are:
Why is this? Meaning, why is my program using so much CPU since it is basically 'sleeping' most of the time (in its current state)?
How can I fix this? The only thing I can think of now is scrapping it and abusing cron jobs to the same effect.
The full code can be found on Github.
-
\$\begingroup\$ Does Rust have a Timer you can use that fires an event periodically? \$\endgroup\$user34073– user340732016年12月30日 05:48:30 +00:00Commented Dec 30, 2016 at 5:48
-
\$\begingroup\$ I don't believe so and the only external timing library's appear to be basic fps counters :/. But i don't think that would work because it needs to monitor "_NET_CURRENT_DESKTOP" which could change at anytime. But i could be wrong? \$\endgroup\$user7008548– user70085482016年12月30日 05:55:44 +00:00Commented Dec 30, 2016 at 5:55
1 Answer 1
since it is basically 'sleeping' most of the time
That's not really the most accurate thing...
impl Duration {
fn new(secs: u64, nanos: u32) -> Duration
}
Duration
's second argument is nanoseconds, thus your "long wait" is 750 nanoseconds. That's 0.75 microseconds / 0.00075 milliseconds / 0.00000075 seconds. For reference, a cycle of a 3GHz computer is 0.3333 nanoseconds. If your code takes zero time, you are waking 1 million times per second!
This is the very definition of a busy wait loop.
So how do you fix it? You need to rearchitect. You state:
and abusing cron jobs to the same effect
Cron jobs have resolution of a minute (×ばつ10^7 bigger!). If you can live with that, sleep for that long.
Another solution is to move to an event-driven system. You make some mention:
it needs to monitor
_NET_CURRENT_DESKTOP
which could change at anytime.
If that's the case, you'd have two sources of events: a timer and whatever provides the _NET_CURRENT_DESKTOP
event. The current front-runners of this type of code in Rust are futures and mio. I don't know exactly how the X code works, but it's probably event driven (GUI code usually is), and there's probably some mio/futures periodic timer. You "simply" need to configure both of them and then respond whenever either triggers.