From f217164dc0c1e4e5f332eb0dbc6b9c024a72fd6d Mon Sep 17 00:00:00 2001 From: trivernis Date: Thu, 3 Dec 2020 12:40:39 +0100 Subject: [PATCH] Add benchmarking of task count Signed-off-by: trivernis --- src/kernel_controller/bench.rs | 24 ++++++++++ src/main.rs | 82 +++++++++++++++++++++++++++++++++- src/output/csv.rs | 17 +++++-- src/output/mod.rs | 1 - 4 files changed, 118 insertions(+), 6 deletions(-) diff --git a/src/kernel_controller/bench.rs b/src/kernel_controller/bench.rs index 43ed9cf..2b86926 100644 --- a/src/kernel_controller/bench.rs +++ b/src/kernel_controller/bench.rs @@ -5,6 +5,7 @@ */ use crate::kernel_controller::KernelController; +use std::fmt::{self, Display, Formatter}; use std::time::{Duration, Instant}; pub struct BenchStatistics { @@ -15,6 +16,28 @@ pub struct BenchStatistics { pub read_duration: Duration, } +impl Display for BenchStatistics { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "Calculation Count: {}\nTask Count: {}\nWrite Duration: {} ms\nGPU Duration: {} ms\nRead Duration: {} ms", + self.calc_count, + self.num_tasks, + self.write_duration.as_secs_f64() * 1000f64, + self.calc_duration.as_secs_f64() * 1000f64, + self.read_duration.as_secs_f64() * 1000f64 + ) + } +} + +impl BenchStatistics { + pub fn avg(&mut self, other: Self) { + self.read_duration = (self.read_duration + other.read_duration) / 2; + self.write_duration = (self.write_duration + other.write_duration) / 2; + self.calc_duration = (self.calc_duration + other.calc_duration) / 2; + } +} + impl KernelController { /// Benches an integer pub fn bench_int(&self, calc_count: u32, num_tasks: usize) -> ocl::Result { @@ -32,6 +55,7 @@ impl KernelController { .kernel_builder("bench_int") .arg(calc_count) .arg(&input_buffer) + .global_work_size(num_tasks) .build()?; let calc_start = Instant::now(); unsafe { diff --git a/src/main.rs b/src/main.rs index d616fce..4c2163f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,10 @@ enum Opts { /// Calculates primes on the GPU #[structopt(name = "calculate-primes")] CalculatePrimes(CalculatePrimes), + + /// Benchmarks the number of tasks used for the calculations + #[structopt(name = "bench-task-count")] + BenchmarkTaskCount(BenchmarkTaskCount), } #[derive(StructOpt, Clone, Debug)] @@ -60,12 +64,41 @@ struct CalculatePrimes { cpu_validate: bool, } +#[derive(StructOpt, Clone, Debug)] +struct BenchmarkTaskCount { + /// How many calculations steps should be done per GPU thread + #[structopt(long = "calculation-steps", default_value = "1000000")] + calculation_steps: u32, + + /// The initial number of tasks for the benchmark + #[structopt(long = "num-tasks-start", default_value = "1")] + num_tasks_start: usize, + + /// The maximum number of tasks for the benchmark + #[structopt(long = "num-tasks-stop", default_value = "10000000")] + num_tasks_stop: usize, + + /// The amount the task number increases per step + #[structopt(long = "num-tasks-step", default_value = "10")] + num_tasks_step: usize, + + /// The average of n runs that is used instead of using one value only. + /// By default the benchmark for each step is only run once + #[structopt(long = "average-of", default_value = "1")] + average_of: usize, + + /// The output file for timings + #[structopt(long = "bench-output", default_value = "bench.csv")] + benchmark_file: PathBuf, +} + fn main() -> ocl::Result<()> { let opts: Opts = Opts::from_args(); let controller = KernelController::new()?; match opts { Opts::CalculatePrimes(prime_opts) => calculate_primes(prime_opts, controller), + Opts::BenchmarkTaskCount(bench_opts) => bench_task_count(bench_opts, controller), } } @@ -95,7 +128,8 @@ fn calculate_primes(prime_opts: CalculatePrimes, controller: KernelController) - "filter_duration", "total_duration", ], - ); + ) + .unwrap(); let (prime_sender, prime_handle) = create_prime_write_thread(output); let (csv_sender, csv_handle) = create_csv_write_thread(timings); @@ -156,12 +190,58 @@ fn calculate_primes(prime_opts: CalculatePrimes, controller: KernelController) - } mem::drop(prime_sender); + mem::drop(csv_sender); prime_handle.join().unwrap(); csv_handle.join().unwrap(); Ok(()) } +fn bench_task_count(opts: BenchmarkTaskCount, controller: KernelController) -> ocl::Result<()> { + let bench_writer = BufWriter::new( + OpenOptions::new() + .truncate(true) + .write(true) + .create(true) + .open(opts.benchmark_file) + .unwrap(), + ); + let csv_writer = CSVWriter::new( + bench_writer, + &[ + "num_tasks", + "calc_count", + "write_duration", + "gpu_duration", + "read_duration", + ], + ) + .unwrap(); + let (bench_sender, bench_handle) = create_csv_write_thread(csv_writer); + for n in (opts.num_tasks_start..opts.num_tasks_stop).step_by(opts.num_tasks_step) { + let mut stats = controller.bench_int(opts.calculation_steps, n)?; + for _ in 1..opts.average_of { + stats.avg(controller.bench_int(opts.calculation_steps, n)?) + } + + println!("{}\n", stats); + bench_sender + .send(vec![ + n.to_string(), + opts.calculation_steps.to_string(), + duration_to_ms_string(&stats.write_duration), + duration_to_ms_string(&stats.calc_duration), + duration_to_ms_string(&stats.read_duration), + ]) + .unwrap(); + } + + mem::drop(bench_sender); + bench_handle.join().unwrap(); + + Ok(()) +} + fn validate_primes_on_cpu(primes: &Vec) { println!("Validating..."); let failures = primes diff --git a/src/output/csv.rs b/src/output/csv.rs index dfaf08e..a8e27db 100644 --- a/src/output/csv.rs +++ b/src/output/csv.rs @@ -17,11 +17,19 @@ where W: Write, { /// Creates a new CSVWriter with a defined list of columns - pub fn new(writer: W, columns: &[&str]) -> Self { - Self { + pub fn new(writer: W, columns: &[&str]) -> Result { + let column_vec = columns + .iter() + .map(|column| column.to_string()) + .collect::>(); + + let mut csv_writer = Self { inner: writer, - columns: columns.iter().map(|column| column.to_string()).collect(), - } + columns: column_vec.clone(), + }; + csv_writer.add_row(column_vec)?; + + Ok(csv_writer) } /// Adds a new row of values to the file @@ -30,6 +38,7 @@ where items .iter() .fold("".to_string(), |a, b| format!("{},{}", a, b)) + .trim_start_matches(',') .as_bytes(), )?; self.inner.write_all("\n".as_bytes()) diff --git a/src/output/mod.rs b/src/output/mod.rs index 96f48c9..4936d2d 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -4,7 +4,6 @@ * See LICENSE for more information */ use crate::output::csv::CSVWriter; -use std::fmt::Display; use std::fs::File; use std::io::{BufWriter, Write}; use std::sync::mpsc::{channel, Sender};