From a89495efb7fd3f493a53f6f2166ff63ac0306441 Mon Sep 17 00:00:00 2001 From: Paul Lange Date: Wed, 15 Jun 2022 15:39:15 +0200 Subject: [PATCH] Replace `docopt` by `clap` --- rayon-demo/Cargo.toml | 6 +- rayon-demo/examples/cpu_monitor.rs | 63 +++++++------- rayon-demo/src/life/mod.rs | 130 +++++++++++++++-------------- rayon-demo/src/matmul/mod.rs | 73 ++++++++-------- rayon-demo/src/mergesort/mod.rs | 61 +++++++------- rayon-demo/src/nbody/mod.rs | 83 ++++++++---------- rayon-demo/src/noop/mod.rs | 41 ++++----- rayon-demo/src/quicksort/mod.rs | 69 ++++++++------- rayon-demo/src/sieve/mod.rs | 76 ++++++++--------- rayon-demo/src/tsp/mod.rs | 61 +++++++------- 10 files changed, 317 insertions(+), 346 deletions(-) diff --git a/rayon-demo/Cargo.toml b/rayon-demo/Cargo.toml index 987eb3aa2..9bb010933 100644 --- a/rayon-demo/Cargo.toml +++ b/rayon-demo/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] rayon = { path = "../" } cgmath = "0.18" -docopt = "1" +clap = { version = "3.2.3", features = ["derive"] } fixedbitset = "0.4" glium = "0.31" lazy_static = "1" @@ -16,10 +16,6 @@ rand = "0.8" rand_xorshift = "0.3" regex = "1" -[dependencies.serde] -version = "1.0.85" -features = ["derive"] - [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/rayon-demo/examples/cpu_monitor.rs b/rayon-demo/examples/cpu_monitor.rs index bbe13164a..dd1ebb04c 100644 --- a/rayon-demo/examples/cpu_monitor.rs +++ b/rayon-demo/examples/cpu_monitor.rs @@ -1,47 +1,40 @@ -use docopt::Docopt; +use clap::{Parser, Subcommand}; use std::io; -use std::process; - -const USAGE: &str = " -Usage: cpu_monitor [options] - cpu_monitor --help +const ABOUT: &str = " A test for monitoring how much CPU usage Rayon consumes under various scenarios. This test is intended to be executed interactively, like so: - cargo run --example cpu_monitor -- tasks_ended - -The list of scenarios you can try are as follows: - -- tasks_ended: after all tasks have finished, go to sleep -- task_stall_root: a root task stalls for a very long time -- task_stall_scope: a task in a scope stalls for a very long time - -Options: - -h, --help Show this message. - -d N, --depth N Control how hard the dummy task works [default: 27] + cargo run --example cpu_monitor -- tasks-ended "; -#[derive(serde::Deserialize)] +#[derive(Subcommand, Debug)] +pub enum Commands { + /// After all tasks have finished, go to sleep + TasksEnded, + /// A root task stalls for a very long time + TaskStallRoot, + /// A task in a scope stalls for a very long time + TaskStallScope, +} + +#[derive(Parser, Debug)] +#[clap(about = ABOUT)] pub struct Args { - arg_scenario: String, - flag_depth: usize, + #[clap(subcommand)] + command: Commands, + + /// Control how hard the dummy task works + #[clap(short = 'd', long, default_value_t = 27)] + depth: usize, } fn main() { - let args: &Args = &Docopt::new(USAGE) - .and_then(|d| d.deserialize()) - .unwrap_or_else(|e| e.exit()); - - match &args.arg_scenario[..] { - "tasks_ended" => tasks_ended(args), - "task_stall_root" => task_stall_root(args), - "task_stall_scope" => task_stall_scope(args), - _ => { - println!("unknown scenario: `{}`", args.arg_scenario); - println!("try --help"); - process::exit(1); - } + let args: Args = Args::from_args(); + match args.command { + Commands::TasksEnded => tasks_ended(&args), + Commands::TaskStallRoot => task_stall_root(&args), + Commands::TaskStallScope => task_stall_scope(&args), } } @@ -58,8 +51,8 @@ fn task(args: &Args) { rayon::join(|| join_recursively(n - 1), || join_recursively(n - 1)); } - println!("Starting heavy work at depth {}...wait.", args.flag_depth); - join_recursively(args.flag_depth); + println!("Starting heavy work at depth {}...wait.", args.depth); + join_recursively(args.depth); println!("Heavy work done; check top. You should see CPU usage drop to zero soon."); println!("Press to quit..."); } diff --git a/rayon-demo/src/life/mod.rs b/rayon-demo/src/life/mod.rs index e2676a1aa..55e81cbfc 100644 --- a/rayon-demo/src/life/mod.rs +++ b/rayon-demo/src/life/mod.rs @@ -1,21 +1,5 @@ -const USAGE: &str = " -Usage: life bench [--size N] [--gens N] [--skip-bridge] - life play [--size N] [--gens N] [--fps N] [--skip-bridge] - life --help -Conway's Game of Life. - -Commands: - bench Run the benchmark in different modes and print the timings. - play Run with a max frame rate and monitor CPU resources. -Options: - --size N Size of the game board (N x N) [default: 200] - --gens N Simulate N generations [default: 100] - --fps N Maximum frame rate [default: 60] - --skip-bridge Skips the tests with par-bridge, as it is much slower. - -h, --help Show this message. -"; - use crate::cpu_time::{self, CpuMeasure}; +use clap::{Parser, Subcommand}; use rand::distributions::Standard; use rand::{thread_rng, Rng}; use std::iter::repeat; @@ -24,21 +8,41 @@ use std::sync::Arc; use std::thread; use std::time::{Duration, Instant}; -use docopt::Docopt; use rayon::iter::ParallelBridge; use rayon::prelude::*; #[cfg(test)] mod bench; -#[derive(serde::Deserialize)] +#[derive(Subcommand)] +enum Commands { + /// Run the benchmark in different modes and print the timings + Bench, + /// Run with a max frame rate and monitor CPU resources + Play, +} + +#[derive(Parser)] +#[clap(about = "Conway's Game of Life")] pub struct Args { - cmd_bench: bool, - cmd_play: bool, - flag_size: usize, - flag_gens: usize, - flag_fps: usize, - flag_skip_bridge: bool, + #[clap(subcommand)] + command: Commands, + + /// Size of the game board (N x N) + #[clap(long, default_value_t = 200)] + size: usize, + + /// Simulate N generations + #[clap(long, default_value_t = 100)] + gens: usize, + + /// Maximum frame rate + #[clap(long, default_value_t = 60)] + fps: usize, + + /// Skips the tests with par-bridge, as it is much slower + #[clap(long)] + skip_bridge: bool, } #[derive(PartialEq, Eq, Clone, Debug)] @@ -233,7 +237,7 @@ fn par_bridge_generations_limited(board: Board, gens: usize, min_interval: Durat } fn measure(f: fn(Board, usize) -> (), args: &Args) -> Duration { - let (n, gens) = (args.flag_size, args.flag_gens); + let (n, gens) = (args.size, args.gens); let brd = Board::new(n, n).random(); let start = Instant::now(); @@ -248,7 +252,7 @@ struct CpuResult { } fn measure_cpu(f: fn(Board, usize, Duration) -> (), args: &Args) -> CpuResult { - let (n, gens, rate) = (args.flag_size, args.flag_gens, args.flag_fps); + let (n, gens, rate) = (args.size, args.gens, args.fps); let interval = Duration::from_secs_f64(1.0 / rate as f64); let brd = Board::new(n, n).random(); @@ -264,50 +268,48 @@ fn measure_cpu(f: fn(Board, usize, Duration) -> (), args: &Args) -> CpuResult { } pub fn main(args: &[String]) { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(args).deserialize()) - .unwrap_or_else(|e| e.exit()); - - if args.cmd_bench { - let serial = measure(generations, &args).as_nanos(); - println!(" serial: {:10} ns", serial); - - let parallel = measure(parallel_generations, &args).as_nanos(); - println!( - "parallel: {:10} ns -> {:.2}x speedup", - parallel, - serial as f64 / parallel as f64 - ); - - if !args.flag_skip_bridge { - let par_bridge = measure(par_bridge_generations, &args).as_nanos(); + let args: Args = Parser::parse_from(args); + match args.command { + Commands::Bench => { + let serial = measure(generations, &args).as_nanos(); + println!(" serial: {:10} ns", serial); + + let parallel = measure(parallel_generations, &args).as_nanos(); println!( - "par_bridge: {:10} ns -> {:.2}x speedup", - par_bridge, - serial as f64 / par_bridge as f64 + "parallel: {:10} ns -> {:.2}x speedup", + parallel, + serial as f64 / parallel as f64 ); - } - } - - if args.cmd_play { - let serial = measure_cpu(generations_limited, &args); - println!(" serial: {:.2} fps", serial.actual_fps); - if let Some(cpu_usage) = serial.cpu_usage_percent { - println!(" cpu usage: {:.1}%", cpu_usage); - } - let parallel = measure_cpu(parallel_generations_limited, &args); - println!("parallel: {:.2} fps", parallel.actual_fps); - if let Some(cpu_usage) = parallel.cpu_usage_percent { - println!(" cpu usage: {:.1}%", cpu_usage); + if !args.skip_bridge { + let par_bridge = measure(par_bridge_generations, &args).as_nanos(); + println!( + "par_bridge: {:10} ns -> {:.2}x speedup", + par_bridge, + serial as f64 / par_bridge as f64 + ); + } } + Commands::Play => { + let serial = measure_cpu(generations_limited, &args); + println!(" serial: {:.2} fps", serial.actual_fps); + if let Some(cpu_usage) = serial.cpu_usage_percent { + println!(" cpu usage: {:.1}%", cpu_usage); + } - if !args.flag_skip_bridge { - let par_bridge = measure_cpu(par_bridge_generations_limited, &args); - println!("par_bridge: {:.2} fps", par_bridge.actual_fps); - if let Some(cpu_usage) = par_bridge.cpu_usage_percent { + let parallel = measure_cpu(parallel_generations_limited, &args); + println!("parallel: {:.2} fps", parallel.actual_fps); + if let Some(cpu_usage) = parallel.cpu_usage_percent { println!(" cpu usage: {:.1}%", cpu_usage); } + + if !args.skip_bridge { + let par_bridge = measure_cpu(par_bridge_generations_limited, &args); + println!("par_bridge: {:.2} fps", par_bridge.actual_fps); + if let Some(cpu_usage) = par_bridge.cpu_usage_percent { + println!(" cpu usage: {:.1}%", cpu_usage); + } + } } } } diff --git a/rayon-demo/src/matmul/mod.rs b/rayon-demo/src/matmul/mod.rs index 24ad5b1e9..a2e9f1c8d 100644 --- a/rayon-demo/src/matmul/mod.rs +++ b/rayon-demo/src/matmul/mod.rs @@ -1,26 +1,8 @@ -const USAGE: &str = " -Usage: matmul bench [--size N] - matmul --help -Parallel matrix multiplication. - -Commands: - bench Run the benchmark in different modes and print the timings. -Options: - --size N Row-size of matrices (rounded up to power of 2) [default: 1024] - -h, --help Show this message. -"; - -#[derive(serde::Deserialize)] -pub struct Args { - cmd_bench: bool, - flag_size: usize, -} +use clap::{Parser, Subcommand}; +use std::time::Instant; -use docopt::Docopt; use rayon::prelude::*; -use std::time::Instant; - // TODO: Investigate other cache patterns for row-major order that may be more // parallelizable. // https://tavianator.com/a-quick-trick-for-faster-naive-matrix-multiplication/ @@ -398,25 +380,42 @@ fn timed_matmul(size: usize, f: F, name: nanos } +#[derive(Subcommand)] +enum Commands { + /// Run the benchmark in different modes and print the timings + Bench, +} + +#[derive(Parser)] +#[clap(about = "Parallel matrix multiplication")] +pub struct Args { + #[clap(subcommand)] + command: Commands, + + /// Row-size of matrices (rounded up to power of 2) + #[clap(long, default_value_t = 1024)] + size: usize, +} + pub fn main(args: &[String]) { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(args).deserialize()) - .unwrap_or_else(|e| e.exit()); - - if args.cmd_bench { - if args.flag_size <= 1024 { - // Crappy algorithm takes several minutes on larger inputs. - timed_matmul(args.flag_size, seq_matmul, "seq row-major"); + let args: Args = Parser::parse_from(args); + + match args.command { + Commands::Bench => { + if args.size <= 1024 { + // Crappy algorithm takes several minutes on larger inputs. + timed_matmul(args.size, seq_matmul, "seq row-major"); + } + let seq = if args.size <= 2048 { + timed_matmul(args.size, seq_matmulz, "seq z-order") + } else { + 0 + }; + let par = timed_matmul(args.size, matmulz, "par z-order"); + timed_matmul(args.size, matmul_strassen, "par strassen"); + let speedup = seq as f64 / par as f64; + println!("speedup: {:.2}x", speedup); } - let seq = if args.flag_size <= 2048 { - timed_matmul(args.flag_size, seq_matmulz, "seq z-order") - } else { - 0 - }; - let par = timed_matmul(args.flag_size, matmulz, "par z-order"); - timed_matmul(args.flag_size, matmul_strassen, "par strassen"); - let speedup = seq as f64 / par as f64; - println!("speedup: {:.2}x", speedup); } } diff --git a/rayon-demo/src/mergesort/mod.rs b/rayon-demo/src/mergesort/mod.rs index a2f48a2df..c5c61cf8b 100644 --- a/rayon-demo/src/mergesort/mod.rs +++ b/rayon-demo/src/mergesort/mod.rs @@ -1,34 +1,16 @@ +use clap::{Parser, Subcommand}; use rand::distributions::Standard; use rand::Rng; +use std::cmp::max; +use std::time::Instant; -const USAGE: &str = " -Usage: mergesort bench [--size N] - mergesort --help - +const ABOUT: &str = " Parallel mergesort: regular sorting with a parallel merge step. O(n log n) time; O(n) extra space; critical path is O(log^3 n) Algorithm described in: https://software.intel.com/en-us/articles/a-parallel-stable-sort-using-c11-for-tbb-cilk-plus-and-openmp - -Commands: - bench Run the benchmark in different modes and print the timings. - -Options: - --size N Number of 32-bit words to sort [default: 250000000] (1GB) - -h, --help Show this message. "; -#[derive(serde::Deserialize)] -pub struct Args { - cmd_bench: bool, - flag_size: usize, -} - -use docopt::Docopt; - -use std::cmp::max; -use std::time::Instant; - pub fn merge_sort(v: &mut [T]) { let n = v.len(); let mut buf = Vec::with_capacity(n); @@ -235,16 +217,33 @@ fn timed_sort(n: usize, f: F, name: &str) -> u64 { nanos } +#[derive(Subcommand)] +enum Commands { + /// Run the benchmark in different modes and print the timings + Bench, +} + +#[derive(Parser)] +#[clap(about = ABOUT)] +pub struct Args { + #[clap(subcommand)] + command: Commands, + + /// Number of 32-bit words to sort + #[clap(long, default_value_t = 250000000)] + size: usize, +} + pub fn main(args: &[String]) { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(args).deserialize()) - .unwrap_or_else(|e| e.exit()); - - if args.cmd_bench { - let seq = timed_sort(args.flag_size, seq_merge_sort, "seq"); - let par = timed_sort(args.flag_size, merge_sort, "par"); - let speedup = seq as f64 / par as f64; - println!("speedup: {:.2}x", speedup); + let args: Args = Parser::parse_from(args); + + match args.command { + Commands::Bench => { + let seq = timed_sort(args.size, seq_merge_sort, "seq"); + let par = timed_sort(args.size, merge_sort, "par"); + let speedup = seq as f64 / par as f64; + println!("speedup: {:.2}x", speedup); + } } } diff --git a/rayon-demo/src/nbody/mod.rs b/rayon-demo/src/nbody/mod.rs index 447bfbfba..4b47a4b5f 100644 --- a/rayon-demo/src/nbody/mod.rs +++ b/rayon-demo/src/nbody/mod.rs @@ -1,4 +1,4 @@ -use docopt::Docopt; +use clap::{Parser, Subcommand, ValueEnum}; use std::time::Instant; #[cfg(test)] @@ -8,70 +8,59 @@ mod visualize; use self::nbody::NBodyBenchmark; use self::visualize::visualize_benchmarks; -const USAGE: &str = " -Usage: nbody bench [--mode MODE --bodies N --ticks N] - nbody visualize [--mode MODE --bodies N] - nbody --help - +const ABOUT: &str = " Physics simulation of multiple bodies alternatively attracting and repelling one another. Visualizable with OpenGL. -Commands: - bench Run the benchmark and print the timings. - visualize Show the graphical visualizer. - -Options: - -h, --help Show this message. - --mode MODE Execution mode for the benchmark/visualizer. - --bodies N Use N bodies [default: 4000]. - --ticks N Simulate for N ticks [default: 100]. - - -Commands: - bench Run the benchmark and print the timings. - visualize Show the graphical visualizer. - -Options: - -h, --help Show this message. - --mode MODE Execution mode for the benchmark/visualizer. - MODE can one of 'par', 'seq', or 'parreduce'. - --bodies N Use N bodies [default: 4000]. - --ticks N Simulate for N ticks [default: 100]. - Ported from the RiverTrail demo found at: https://github.com/IntelLabs/RiverTrail/tree/master/examples/nbody-webgl "; -#[derive(Copy, Clone, PartialEq, Eq, serde::Deserialize)] +#[derive(Subcommand)] +enum Commands { + /// Run the benchmark and print the timings + Bench, + /// Show the graphical visualizer + Visualize, +} + +#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)] +#[clap(rename_all = "lower")] pub enum ExecutionMode { Par, ParReduce, Seq, } -#[derive(serde::Deserialize)] +#[derive(Parser)] +#[clap(about = ABOUT)] pub struct Args { - cmd_bench: bool, - cmd_visualize: bool, - flag_mode: Option, - flag_bodies: usize, - flag_ticks: usize, + #[clap(subcommand)] + command: Commands, + + /// Use N bodies + #[clap(long, default_value_t = 4000)] + bodies: usize, + + /// Simulate for N ticks + #[clap(long, default_value_t = 100)] + ticks: usize, + + /// Execution mode for the benchmark/visualizer + #[clap(value_enum, long)] + mode: Option, } pub fn main(args: &[String]) { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(args).deserialize()) - .unwrap_or_else(|e| e.exit()); + let args: Args = Parser::parse_from(args); - if args.cmd_bench { - run_benchmarks(args.flag_mode, args.flag_bodies, args.flag_ticks); - } - - if args.cmd_visualize { - visualize_benchmarks( - args.flag_bodies, - args.flag_mode.unwrap_or(ExecutionMode::Par), - ); + match args.command { + Commands::Bench => { + run_benchmarks(args.mode, args.bodies, args.ticks); + } + Commands::Visualize => { + visualize_benchmarks(args.bodies, args.mode.unwrap_or(ExecutionMode::Par)); + } } } diff --git a/rayon-demo/src/noop/mod.rs b/rayon-demo/src/noop/mod.rs index 75cf8d93c..f0f58e9bd 100644 --- a/rayon-demo/src/noop/mod.rs +++ b/rayon-demo/src/noop/mod.rs @@ -1,36 +1,31 @@ -const USAGE: &str = " -Usage: noop [--sleep N] [--iters N] - -Noop loop to measure CPU usage. See rayon-rs/rayon#642. - -Options: - --sleep N How long to sleep (in millis) between doing a spawn. [default: 10] - --iters N Total time to execution (in millis). [default: 100] -"; - use crate::cpu_time; -use docopt::Docopt; +use clap::Parser; -#[derive(serde::Deserialize)] +#[derive(Parser)] +#[clap( + author, + version, + about = "Noop loop to measure CPU usage. See rayon-rs/rayon#642." +)] pub struct Args { - flag_sleep: u64, - flag_iters: u64, + /// How long to sleep (in millis) between doing a spawn. + #[clap(long, default_value_t = 10)] + sleep: u64, + + /// Total time to execution (in millis). + #[clap(long, default_value_t = 100)] + iters: u64, } pub fn main(args: &[String]) { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(args).deserialize()) - .unwrap_or_else(|e| e.exit()); + let args: Args = Parser::parse_from(args); let m = cpu_time::measure_cpu(|| { - for _ in 1..args.flag_iters { - std::thread::sleep(std::time::Duration::from_millis(args.flag_sleep)); + for _ in 1..args.iters { + std::thread::sleep(std::time::Duration::from_millis(args.sleep)); rayon::spawn(move || {}); } }); - println!( - "noop --iters={} --sleep={}", - args.flag_iters, args.flag_sleep - ); + println!("noop --iters={} --sleep={}", args.iters, args.sleep); cpu_time::print_time(m); } diff --git a/rayon-demo/src/quicksort/mod.rs b/rayon-demo/src/quicksort/mod.rs index b0ced3fa0..51d109950 100644 --- a/rayon-demo/src/quicksort/mod.rs +++ b/rayon-demo/src/quicksort/mod.rs @@ -1,28 +1,6 @@ #![allow(non_camel_case_types)] -const USAGE: &str = " -Usage: quicksort bench [options] - quicksort --help - -Parallel quicksort. Only the main recursive step is parallelized. - -Commands: - bench Run the benchmark in different modes and print the timings. - -Options: - --size N Number of 32-bit words to sort [default: 250000000] (1GB) - --par-only Skip the sequential sort. - -h, --help Show this message. -"; - -#[derive(serde::Deserialize)] -pub struct Args { - cmd_bench: bool, - flag_size: usize, - flag_par_only: bool, -} - -use docopt::Docopt; +use clap::{Parser, Subcommand}; use rand::distributions::Standard; use rand::Rng; use std::time::Instant; @@ -126,19 +104,40 @@ fn timed_sort(n: usize, f: F, name: &str) -> u64 { nanos } +#[derive(Subcommand)] +enum Commands { + /// Run the benchmark in different modes and print the timings + Bench, +} + +#[derive(Parser)] +#[clap(about = "Parallel quicksort. Only the main recursive step is parallelized.")] +pub struct Args { + #[clap(subcommand)] + command: Commands, + + /// Number of 32-bit words to sort + #[clap(long, default_value_t = 250000000)] + size: usize, + + /// Skip the sequential sort. + #[clap(long, default_value_t = false)] + par_only: bool, +} + pub fn main(args: &[String]) { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(args).deserialize()) - .unwrap_or_else(|e| e.exit()); - - if args.cmd_bench { - if args.flag_par_only { - timed_sort(args.flag_size, quick_sort::, "par"); - } else { - let seq = timed_sort(args.flag_size, quick_sort::, "seq"); - let par = timed_sort(args.flag_size, quick_sort::, "par"); - let speedup = seq as f64 / par as f64; - println!("speedup: {:.2}x", speedup); + let args: Args = Parser::parse_from(args); + + match args.command { + Commands::Bench => { + if args.par_only { + timed_sort(args.size, quick_sort::, "par"); + } else { + let seq = timed_sort(args.size, quick_sort::, "seq"); + let par = timed_sort(args.size, quick_sort::, "par"); + let speedup = seq as f64 / par as f64; + println!("speedup: {:.2}x", speedup); + } } } } diff --git a/rayon-demo/src/sieve/mod.rs b/rayon-demo/src/sieve/mod.rs index 4417d83d7..60089fc3a 100644 --- a/rayon-demo/src/sieve/mod.rs +++ b/rayon-demo/src/sieve/mod.rs @@ -1,7 +1,4 @@ -const USAGE: &str = " -Usage: sieve bench - sieve --help - +const ABOUT: &str = " Sieve of Eratosthenes A sieve finds prime numbers by first assuming all candidates are prime, @@ -23,28 +20,16 @@ locality. Since the sieve has to sweep repeatedly over the same memory, it's helpful that the chunk size can fit entirely in the CPU cache. Then `sieve_parallel` also benefits from this as long as there's cache room for multiple chunks, for the separate jobs in each thread. - -Commands: - bench Run the benchmark in different modes and print the timings. - -Options: - -h, --help Show this message. "; - -#[derive(serde::Deserialize)] -pub struct Args { - cmd_bench: bool, -} +const CHUNK_SIZE: usize = 100_000; #[cfg(test)] mod bench; -use docopt::Docopt; +use clap::{Parser, Subcommand}; use rayon::prelude::*; use std::time::{Duration, Instant}; -const CHUNK_SIZE: usize = 100_000; - // Number of Primes < 10^n // https://oeis.org/A006880 const NUM_PRIMES: &[usize] = &[ @@ -181,27 +166,40 @@ fn measure(f: fn(usize) -> Vec) -> Duration { duration } +#[derive(Subcommand)] +enum Commands { + /// Run the benchmark in different modes and print the timings + Bench, +} + +#[derive(Parser)] +#[clap(about = ABOUT)] +pub struct Args { + #[clap(subcommand)] + command: Commands, +} + pub fn main(args: &[String]) { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(args).deserialize()) - .unwrap_or_else(|e| e.exit()); - - if args.cmd_bench { - let serial = measure(sieve_serial).as_nanos(); - println!(" serial: {:10} ns", serial); - - let chunks = measure(sieve_chunks).as_nanos(); - println!( - " chunks: {:10} ns -> {:.2}x speedup", - chunks, - serial as f64 / chunks as f64 - ); - - let parallel = measure(sieve_parallel).as_nanos(); - println!( - "parallel: {:10} ns -> {:.2}x speedup", - parallel, - chunks as f64 / parallel as f64 - ); + let args: Args = Parser::parse_from(args); + + match args.command { + Commands::Bench => { + let serial = measure(sieve_serial).as_nanos(); + println!(" serial: {:10} ns", serial); + + let chunks = measure(sieve_chunks).as_nanos(); + println!( + " chunks: {:10} ns -> {:.2}x speedup", + chunks, + serial as f64 / chunks as f64 + ); + + let parallel = measure(sieve_parallel).as_nanos(); + println!( + "parallel: {:10} ns -> {:.2}x speedup", + parallel, + chunks as f64 / parallel as f64 + ); + } } } diff --git a/rayon-demo/src/tsp/mod.rs b/rayon-demo/src/tsp/mod.rs index 5c0581081..1bbc7aca6 100644 --- a/rayon-demo/src/tsp/mod.rs +++ b/rayon-demo/src/tsp/mod.rs @@ -3,11 +3,11 @@ //! Based on code developed at ETH by Christoph von Praun, Florian //! Schneider, Nicholas Matsakis, and Thomas Gross. -use docopt::Docopt; +use clap::{Parser, Subcommand}; use std::error::Error; use std::fs::File; use std::io::prelude::*; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::time::Instant; #[cfg(test)] @@ -22,45 +22,46 @@ mod weight; use self::graph::{Graph, Node}; use self::solver::SolverCx; -const USAGE: &str = " -Usage: tsp bench [--seq-threshold N] [--from N] - +const ABOUT: &str = " Parallel traveling salesman problem solver. Data input is expected to be in TSPLIB format. Suggested command: cargo run --release -- tsp bench data/tsp/dj15.tsp --seq-threshold 8 +"; -Commands: - bench Run the benchmark and print the timings. +#[derive(Subcommand)] +enum Commands { + /// Run the benchmark and print the timings + Bench { + #[clap(value_parser)] + datafile: PathBuf, + }, +} -Options: - -h, --help Show this message. - --seq-threshold N Adjust sequential fallback threshold [default: 10]. - Fall back to seq search when there are N or fewer nodes remaining. - Lower values of N mean more parallelism. - --from N Node index from which to start the search [default: 0]. -"; +#[derive(Parser)] +#[clap(about = ABOUT)] +struct Args { + #[clap(subcommand)] + command: Commands, -#[derive(serde::Deserialize)] -pub struct Args { - cmd_bench: bool, - arg_datafile: String, - flag_seq_threshold: usize, - flag_from: usize, + /// Node index from which to start the search + #[clap(long, default_value_t = 0)] + from: usize, + + /// Adjust sequential fallback threshold. Fall back to seq search when there + /// are N or fewer nodes remaining. Lower values of N mean more parallelism. + #[clap(long, default_value_t = 10)] + seq_threshold: usize, } pub fn main(args: &[String]) { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(args).deserialize()) - .unwrap_or_else(|e| e.exit()); - - if args.cmd_bench { - let _ = run_solver( - Path::new(&args.arg_datafile), - args.flag_seq_threshold, - args.flag_from, - ); + let args: Args = Parser::parse_from(args); + + match args.command { + Commands::Bench { datafile } => { + let _ = run_solver(&datafile, args.seq_threshold, args.from); + } } }