aboutsummaryrefslogtreecommitdiff
path: root/src/plot/gnuplot_backend/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/plot/gnuplot_backend/mod.rs')
-rwxr-xr-xsrc/plot/gnuplot_backend/mod.rs254
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)
+ );
+ }
+}