Skip to content

Commit dda3f18

Browse files
committed
allow running life demo with fps limit
1 parent df86443 commit dda3f18

File tree

5 files changed

+126
-2
lines changed

5 files changed

+126
-2
lines changed

rayon-demo/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ regex = "1"
1717
serde = "1"
1818
serde_derive = "1"
1919
time = "0.1"
20+
winapi = { version = "0.3", features = ["processthreadsapi"] }
2021

2122
[dev-dependencies]
2223
num = "0.2"

rayon-demo/src/life/cpu_time/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use time::Duration;
2+
3+
#[cfg(windows)]
4+
mod win;
5+
#[cfg(windows)]
6+
pub use self::win::get_cpu_time;
7+
8+
#[cfg(not(windows))]
9+
pub fn get_cpu_time() -> Option<u64> {
10+
None
11+
}
12+
13+
pub fn get_cpu_duration(start: Option<u64>, stop: Option<u64>) -> Option<Duration> {
14+
start.and_then(|start| stop.and_then(|stop| Some(Duration::nanoseconds((stop - start) as i64))))
15+
}

rayon-demo/src/life/cpu_time/win.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use std::mem;
2+
use winapi::shared::minwindef::FILETIME;
3+
use winapi::um::processthreadsapi::{GetCurrentProcess, GetProcessTimes};
4+
5+
pub fn get_cpu_time() -> Option<u64> {
6+
unsafe {
7+
let process = GetCurrentProcess();
8+
let mut _creation: FILETIME = mem::uninitialized();
9+
let mut _exit: FILETIME = mem::uninitialized();
10+
let mut kernel: FILETIME = mem::uninitialized();
11+
let mut user: FILETIME = mem::uninitialized();
12+
GetProcessTimes(process, &mut _creation, &mut _exit, &mut kernel, &mut user);
13+
let kernel = (kernel.dwHighDateTime as u64) << 32 | kernel.dwLowDateTime as u64;
14+
let user = (user.dwHighDateTime as u64) << 32 | user.dwLowDateTime as u64;
15+
Some(100 * (kernel + user))
16+
}
17+
}

rayon-demo/src/life/mod.rs

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
const USAGE: &'static str = "
22
Usage: life bench [--size N] [--gens N]
3+
life play [--size N] [--gens N] [--fps N]
34
life --help
45
Conway's Game of Life.
56
67
Commands:
78
bench Run the benchmark in different modes and print the timings.
9+
play Run with a max frame rate and monitor CPU resources.
810
Options:
911
--size N Size of the game board (N x N) [default: 200]
1012
--gens N Simulate N generations [default: 100]
13+
--fps N Maximum frame rate [default: 60]
1114
-h, --help Show this message.
1215
";
1316

@@ -17,6 +20,7 @@ use rand::distributions::Standard;
1720
use std::iter::repeat;
1821
use std::num::Wrapping;
1922
use std::sync::Arc;
23+
use std::thread;
2024
use time;
2125

2226
use docopt::Docopt;
@@ -25,12 +29,15 @@ use rayon::iter::ParallelBridge;
2529

2630
#[cfg(test)]
2731
mod bench;
32+
mod cpu_time;
2833

2934
#[derive(Deserialize)]
3035
pub struct Args {
3136
cmd_bench: bool,
37+
cmd_play: bool,
3238
flag_size: usize,
3339
flag_gens: usize,
40+
flag_fps: usize,
3441
}
3542

3643
#[derive(PartialEq, Eq, Clone, Debug)]
@@ -161,6 +168,44 @@ fn par_bridge_generations(board: Board, gens: usize) {
161168
for _ in 0..gens { brd = brd.par_bridge_next_generation(); }
162169
}
163170

171+
fn delay(last_start: u64, min_interval_ns: u64) -> u64 {
172+
let mut current_time = time::precise_time_ns();
173+
let elapsed = current_time - last_start;
174+
if elapsed < min_interval_ns {
175+
let delay = min_interval_ns - elapsed;
176+
thread::sleep(::std::time::Duration::from_nanos(delay));
177+
current_time += delay;
178+
}
179+
current_time
180+
}
181+
182+
fn generations_limited(board: Board, gens: usize, min_interval_ns: u64) {
183+
let mut brd = board;
184+
let mut time = time::precise_time_ns();
185+
for _ in 0..gens {
186+
brd = brd.next_generation();
187+
time = delay(time, min_interval_ns);
188+
}
189+
}
190+
191+
fn parallel_generations_limited(board: Board, gens: usize, min_interval_ns: u64) {
192+
let mut brd = board;
193+
let mut time = time::precise_time_ns();
194+
for _ in 0..gens {
195+
brd = brd.parallel_next_generation();
196+
time = delay(time, min_interval_ns);
197+
}
198+
}
199+
200+
fn par_bridge_generations_limited(board: Board, gens: usize, min_interval_ns: u64) {
201+
let mut brd = board;
202+
let mut time = time::precise_time_ns();
203+
for _ in 0..gens {
204+
brd = brd.par_bridge_next_generation();
205+
time = delay(time, min_interval_ns);
206+
}
207+
}
208+
164209
fn measure(f: fn(Board, usize) -> (), args: &Args) -> u64 {
165210
let (n, gens) = (args.flag_size, args.flag_gens);
166211
let brd = Board::new(n, n).random();
@@ -171,6 +216,31 @@ fn measure(f: fn(Board, usize) -> (), args: &Args) -> u64 {
171216
time::precise_time_ns() - start
172217
}
173218

219+
struct CpuResult {
220+
actual_fps: f64,
221+
cpu_usage_percent: Option<f64>,
222+
}
223+
224+
fn measure_cpu(f: fn(Board, usize, u64) -> (), args: &Args) -> CpuResult {
225+
let (n, gens, rate) = (args.flag_size, args.flag_gens, args.flag_fps);
226+
let interval = 1_000_000_000 / rate as u64;
227+
let brd = Board::new(n, n).random();
228+
let start = time::precise_time_ns();
229+
let cpu_start = cpu_time::get_cpu_time();
230+
231+
f(brd, gens, interval);
232+
233+
let cpu_stop = cpu_time::get_cpu_time();
234+
let duration = time::precise_time_ns() - start;
235+
236+
CpuResult {
237+
actual_fps: (1_000_000_000.0 * gens as f64) / duration as f64,
238+
cpu_usage_percent: cpu_time::get_cpu_duration(cpu_start, cpu_stop)
239+
.and_then(|cpu| cpu.num_nanoseconds())
240+
.and_then(|cpu| Some(100.0 * cpu as f64 / duration as f64)),
241+
}
242+
}
243+
174244
pub fn main(args: &[String]) {
175245
let args: Args =
176246
Docopt::new(USAGE)
@@ -183,10 +253,30 @@ pub fn main(args: &[String]) {
183253

184254
let parallel = measure(parallel_generations, &args);
185255
println!("parallel: {:10} ns -> {:.2}x speedup", parallel,
186-
serial as f64 / parallel as f64);
256+
serial as f64 / parallel as f64);
187257

188258
let par_bridge = measure(par_bridge_generations, &args);
189259
println!("par_bridge: {:10} ns -> {:.2}x speedup", par_bridge,
190-
serial as f64 / par_bridge as f64);
260+
serial as f64 / par_bridge as f64);
261+
}
262+
263+
if args.cmd_play {
264+
let serial = measure_cpu(generations_limited, &args);
265+
println!(" serial: {:.2} fps", serial.actual_fps);
266+
if let Some(cpu_usage) = serial.cpu_usage_percent {
267+
println!(" cpu usage: {:.1}%", cpu_usage);
268+
}
269+
270+
let parallel = measure_cpu(parallel_generations_limited, &args);
271+
println!("parallel: {:.2} fps", parallel.actual_fps);
272+
if let Some(cpu_usage) = parallel.cpu_usage_percent {
273+
println!(" cpu usage: {:.1}%", cpu_usage);
274+
}
275+
276+
let par_bridge = measure_cpu(par_bridge_generations_limited, &args);
277+
println!("par_bridge: {:.2} fps", par_bridge.actual_fps);
278+
if let Some(cpu_usage) = par_bridge.cpu_usage_percent {
279+
println!(" cpu usage: {:.1}%", cpu_usage);
280+
}
191281
}
192282
}

rayon-demo/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ extern crate num; // factorial
4242
extern crate lazy_static; // find
4343
extern crate fixedbitset; // tsp
4444
extern crate regex; // tsp
45+
extern crate winapi; // life
4546

4647
#[cfg(test)]
4748
extern crate test;

0 commit comments

Comments
 (0)