Add benchmarking of task count

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/1/head
trivernis 4 years ago
parent bd58d5fd1a
commit f217164dc0
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -5,6 +5,7 @@
*/ */
use crate::kernel_controller::KernelController; use crate::kernel_controller::KernelController;
use std::fmt::{self, Display, Formatter};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
pub struct BenchStatistics { pub struct BenchStatistics {
@ -15,6 +16,28 @@ pub struct BenchStatistics {
pub read_duration: Duration, 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 { impl KernelController {
/// Benches an integer /// Benches an integer
pub fn bench_int(&self, calc_count: u32, num_tasks: usize) -> ocl::Result<BenchStatistics> { pub fn bench_int(&self, calc_count: u32, num_tasks: usize) -> ocl::Result<BenchStatistics> {
@ -32,6 +55,7 @@ impl KernelController {
.kernel_builder("bench_int") .kernel_builder("bench_int")
.arg(calc_count) .arg(calc_count)
.arg(&input_buffer) .arg(&input_buffer)
.global_work_size(num_tasks)
.build()?; .build()?;
let calc_start = Instant::now(); let calc_start = Instant::now();
unsafe { unsafe {

@ -25,6 +25,10 @@ enum Opts {
/// Calculates primes on the GPU /// Calculates primes on the GPU
#[structopt(name = "calculate-primes")] #[structopt(name = "calculate-primes")]
CalculatePrimes(CalculatePrimes), CalculatePrimes(CalculatePrimes),
/// Benchmarks the number of tasks used for the calculations
#[structopt(name = "bench-task-count")]
BenchmarkTaskCount(BenchmarkTaskCount),
} }
#[derive(StructOpt, Clone, Debug)] #[derive(StructOpt, Clone, Debug)]
@ -60,12 +64,41 @@ struct CalculatePrimes {
cpu_validate: bool, 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<()> { fn main() -> ocl::Result<()> {
let opts: Opts = Opts::from_args(); let opts: Opts = Opts::from_args();
let controller = KernelController::new()?; let controller = KernelController::new()?;
match opts { match opts {
Opts::CalculatePrimes(prime_opts) => calculate_primes(prime_opts, controller), 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", "filter_duration",
"total_duration", "total_duration",
], ],
); )
.unwrap();
let (prime_sender, prime_handle) = create_prime_write_thread(output); let (prime_sender, prime_handle) = create_prime_write_thread(output);
let (csv_sender, csv_handle) = create_csv_write_thread(timings); 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(prime_sender);
mem::drop(csv_sender);
prime_handle.join().unwrap(); prime_handle.join().unwrap();
csv_handle.join().unwrap(); csv_handle.join().unwrap();
Ok(()) 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<u64>) { fn validate_primes_on_cpu(primes: &Vec<u64>) {
println!("Validating..."); println!("Validating...");
let failures = primes let failures = primes

@ -17,11 +17,19 @@ where
W: Write, W: Write,
{ {
/// Creates a new CSVWriter with a defined list of columns /// Creates a new CSVWriter with a defined list of columns
pub fn new(writer: W, columns: &[&str]) -> Self { pub fn new(writer: W, columns: &[&str]) -> Result<Self> {
Self { let column_vec = columns
.iter()
.map(|column| column.to_string())
.collect::<Vec<String>>();
let mut csv_writer = Self {
inner: writer, 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 /// Adds a new row of values to the file
@ -30,6 +38,7 @@ where
items items
.iter() .iter()
.fold("".to_string(), |a, b| format!("{},{}", a, b)) .fold("".to_string(), |a, b| format!("{},{}", a, b))
.trim_start_matches(',')
.as_bytes(), .as_bytes(),
)?; )?;
self.inner.write_all("\n".as_bytes()) self.inner.write_all("\n".as_bytes())

@ -4,7 +4,6 @@
* See LICENSE for more information * See LICENSE for more information
*/ */
use crate::output::csv::CSVWriter; use crate::output::csv::CSVWriter;
use std::fmt::Display;
use std::fs::File; use std::fs::File;
use std::io::{BufWriter, Write}; use std::io::{BufWriter, Write};
use std::sync::mpsc::{channel, Sender}; use std::sync::mpsc::{channel, Sender};

Loading…
Cancel
Save