diff options
Diffstat (limited to 'src/plot/gnuplot_backend/mod.rs')
-rwxr-xr-x | src/plot/gnuplot_backend/mod.rs | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/src/plot/gnuplot_backend/mod.rs b/src/plot/gnuplot_backend/mod.rs new file mode 100755 index 0000000..e902d47 --- /dev/null +++ b/src/plot/gnuplot_backend/mod.rs @@ -0,0 +1,254 @@ +use std::iter; +use std::path::PathBuf; +use std::process::Child; + +use crate::stats::univariate::Sample; +use criterion_plot::prelude::*; + +mod distributions; +mod iteration_times; +mod pdf; +mod regression; +mod summary; +mod t_test; +use self::distributions::*; +use self::iteration_times::*; +use self::pdf::*; +use self::regression::*; +use self::summary::*; +use self::t_test::*; + +use crate::measurement::ValueFormatter; +use crate::report::{BenchmarkId, ValueType}; +use crate::stats::bivariate::Data; + +use super::{PlotContext, PlotData, Plotter}; +use crate::format; + +fn gnuplot_escape(string: &str) -> String { + string.replace("_", "\\_").replace("'", "''") +} + +static DEFAULT_FONT: &str = "Helvetica"; +static KDE_POINTS: usize = 500; +static SIZE: Size = Size(1280, 720); + +const LINEWIDTH: LineWidth = LineWidth(2.); +const POINT_SIZE: PointSize = PointSize(0.75); + +const DARK_BLUE: Color = Color::Rgb(31, 120, 180); +const DARK_ORANGE: Color = Color::Rgb(255, 127, 0); +const DARK_RED: Color = Color::Rgb(227, 26, 28); + +fn debug_script(path: &PathBuf, figure: &Figure) { + if crate::debug_enabled() { + let mut script_path = path.clone(); + script_path.set_extension("gnuplot"); + info!("Writing gnuplot script to {:?}", script_path); + let result = figure.save(script_path.as_path()); + if let Err(e) = result { + error!("Failed to write debug output: {}", e); + } + } +} + +/// Private +trait Append<T> { + /// Private + fn append_(self, item: T) -> Self; +} + +// NB I wish this was in the standard library +impl<T> Append<T> for Vec<T> { + fn append_(mut self, item: T) -> Vec<T> { + self.push(item); + self + } +} + +#[derive(Default)] +pub(crate) struct Gnuplot { + process_list: Vec<Child>, +} + +impl Plotter for Gnuplot { + fn pdf(&mut self, ctx: PlotContext<'_>, data: PlotData<'_>) { + let size = ctx.size.map(|(w, h)| Size(w, h)); + self.process_list.push(if ctx.is_thumbnail { + if let Some(cmp) = data.comparison { + pdf_comparison_small( + ctx.id, + ctx.context, + data.formatter, + data.measurements, + cmp, + size, + ) + } else { + pdf_small(ctx.id, ctx.context, data.formatter, data.measurements, size) + } + } else if let Some(cmp) = data.comparison { + pdf_comparison( + ctx.id, + ctx.context, + data.formatter, + data.measurements, + cmp, + size, + ) + } else { + pdf(ctx.id, ctx.context, data.formatter, data.measurements, size) + }); + } + + fn regression(&mut self, ctx: PlotContext<'_>, data: PlotData<'_>) { + let size = ctx.size.map(|(w, h)| Size(w, h)); + self.process_list.push(if ctx.is_thumbnail { + if let Some(cmp) = data.comparison { + let base_data = Data::new(&cmp.base_iter_counts, &cmp.base_sample_times); + regression_comparison_small( + ctx.id, + ctx.context, + data.formatter, + data.measurements, + cmp, + &base_data, + size, + ) + } else { + regression_small(ctx.id, ctx.context, data.formatter, data.measurements, size) + } + } else if let Some(cmp) = data.comparison { + let base_data = Data::new(&cmp.base_iter_counts, &cmp.base_sample_times); + regression_comparison( + ctx.id, + ctx.context, + data.formatter, + data.measurements, + cmp, + &base_data, + size, + ) + } else { + regression(ctx.id, ctx.context, data.formatter, data.measurements, size) + }); + } + + fn iteration_times(&mut self, ctx: PlotContext<'_>, data: PlotData<'_>) { + let size = ctx.size.map(|(w, h)| Size(w, h)); + self.process_list.push(if ctx.is_thumbnail { + if let Some(cmp) = data.comparison { + iteration_times_comparison_small( + ctx.id, + ctx.context, + data.formatter, + data.measurements, + cmp, + size, + ) + } else { + iteration_times_small(ctx.id, ctx.context, data.formatter, data.measurements, size) + } + } else if let Some(cmp) = data.comparison { + iteration_times_comparison( + ctx.id, + ctx.context, + data.formatter, + data.measurements, + cmp, + size, + ) + } else { + iteration_times(ctx.id, ctx.context, data.formatter, data.measurements, size) + }); + } + + fn abs_distributions(&mut self, ctx: PlotContext<'_>, data: PlotData<'_>) { + let size = ctx.size.map(|(w, h)| Size(w, h)); + self.process_list.extend(abs_distributions( + ctx.id, + ctx.context, + data.formatter, + data.measurements, + size, + )); + } + + fn rel_distributions(&mut self, ctx: PlotContext<'_>, data: PlotData<'_>) { + let size = ctx.size.map(|(w, h)| Size(w, h)); + if let Some(cmp) = data.comparison { + self.process_list.extend(rel_distributions( + ctx.id, + ctx.context, + data.measurements, + cmp, + size, + )); + } else { + error!("Comparison data is not provided for a relative distribution figure"); + } + } + + fn t_test(&mut self, ctx: PlotContext<'_>, data: PlotData<'_>) { + let size = ctx.size.map(|(w, h)| Size(w, h)); + if let Some(cmp) = data.comparison { + self.process_list + .push(t_test(ctx.id, ctx.context, data.measurements, cmp, size)); + } else { + error!("Comparison data is not provided for t_test plot"); + } + } + + fn line_comparison( + &mut self, + ctx: PlotContext<'_>, + formatter: &dyn ValueFormatter, + all_curves: &[&(&BenchmarkId, Vec<f64>)], + value_type: ValueType, + ) { + let path = ctx.line_comparison_path(); + self.process_list.push(line_comparison( + formatter, + ctx.id.as_title(), + all_curves, + &path, + value_type, + ctx.context.plot_config.summary_scale, + )); + } + + fn violin( + &mut self, + ctx: PlotContext<'_>, + formatter: &dyn ValueFormatter, + all_curves: &[&(&BenchmarkId, Vec<f64>)], + ) { + let violin_path = ctx.violin_path(); + + self.process_list.push(violin( + formatter, + ctx.id.as_title(), + all_curves, + &violin_path, + ctx.context.plot_config.summary_scale, + )); + } + + fn wait(&mut self) { + let start = std::time::Instant::now(); + let child_count = self.process_list.len(); + for child in self.process_list.drain(..) { + match child.wait_with_output() { + Ok(ref out) if out.status.success() => {} + Ok(out) => error!("Error in Gnuplot: {}", String::from_utf8_lossy(&out.stderr)), + Err(e) => error!("Got IO error while waiting for Gnuplot to complete: {}", e), + } + } + let elapsed = &start.elapsed(); + info!( + "Waiting for {} gnuplot processes took {}", + child_count, + format::time(crate::DurationExt::to_nanos(elapsed) as f64) + ); + } +} |