aboutsummaryrefslogtreecommitdiff
path: root/src/plot/gnuplot_backend/pdf.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/plot/gnuplot_backend/pdf.rs')
-rwxr-xr-xsrc/plot/gnuplot_backend/pdf.rs392
1 files changed, 392 insertions, 0 deletions
diff --git a/src/plot/gnuplot_backend/pdf.rs b/src/plot/gnuplot_backend/pdf.rs
new file mode 100755
index 0000000..3ee2360
--- /dev/null
+++ b/src/plot/gnuplot_backend/pdf.rs
@@ -0,0 +1,392 @@
+use super::*;
+use crate::kde;
+use crate::measurement::ValueFormatter;
+use crate::report::{BenchmarkId, ComparisonData, MeasurementData, ReportContext};
+use std::process::Child;
+
+pub(crate) fn pdf(
+ id: &BenchmarkId,
+ context: &ReportContext,
+ formatter: &dyn ValueFormatter,
+ measurements: &MeasurementData<'_>,
+ size: Option<Size>,
+) -> Child {
+ let avg_times = &measurements.avg_times;
+ let typical = avg_times.max();
+ let mut scaled_avg_times: Vec<f64> = (avg_times as &Sample<f64>).iter().cloned().collect();
+ let unit = formatter.scale_values(typical, &mut scaled_avg_times);
+ let scaled_avg_times = Sample::new(&scaled_avg_times);
+
+ let mean = scaled_avg_times.mean();
+
+ let iter_counts = measurements.iter_counts();
+ let &max_iters = iter_counts
+ .iter()
+ .max_by_key(|&&iters| iters as u64)
+ .unwrap();
+ let exponent = (max_iters.log10() / 3.).floor() as i32 * 3;
+ let y_scale = 10f64.powi(-exponent);
+
+ let y_label = if exponent == 0 {
+ "Iterations".to_owned()
+ } else {
+ format!("Iterations (x 10^{})", exponent)
+ };
+
+ let (xs, ys) = kde::sweep(&scaled_avg_times, KDE_POINTS, None);
+ let (lost, lomt, himt, hist) = avg_times.fences();
+ let mut fences = [lost, lomt, himt, hist];
+ let _ = formatter.scale_values(typical, &mut fences);
+ let [lost, lomt, himt, hist] = fences;
+
+ let vertical = &[0., max_iters];
+ let zeros = iter::repeat(0);
+
+ let mut figure = Figure::new();
+ figure
+ .set(Font(DEFAULT_FONT))
+ .set(size.unwrap_or(SIZE))
+ .configure(Axis::BottomX, |a| {
+ let xs_ = Sample::new(&xs);
+ a.set(Label(format!("Average time ({})", unit)))
+ .set(Range::Limits(xs_.min(), xs_.max()))
+ })
+ .configure(Axis::LeftY, |a| {
+ a.set(Label(y_label))
+ .set(Range::Limits(0., max_iters * y_scale))
+ .set(ScaleFactor(y_scale))
+ })
+ .configure(Axis::RightY, |a| a.set(Label("Density (a.u.)")))
+ .configure(Key, |k| {
+ k.set(Justification::Left)
+ .set(Order::SampleText)
+ .set(Position::Outside(Vertical::Top, Horizontal::Right))
+ })
+ .plot(
+ FilledCurve {
+ x: &*xs,
+ y1: &*ys,
+ y2: zeros,
+ },
+ |c| {
+ c.set(Axes::BottomXRightY)
+ .set(DARK_BLUE)
+ .set(Label("PDF"))
+ .set(Opacity(0.25))
+ },
+ )
+ .plot(
+ Lines {
+ x: &[mean, mean],
+ y: vertical,
+ },
+ |c| {
+ c.set(DARK_BLUE)
+ .set(LINEWIDTH)
+ .set(LineType::Dash)
+ .set(Label("Mean"))
+ },
+ )
+ .plot(
+ Points {
+ x: avg_times
+ .iter()
+ .zip(scaled_avg_times.iter())
+ .filter_map(
+ |((_, label), t)| {
+ if label.is_outlier() {
+ None
+ } else {
+ Some(t)
+ }
+ },
+ ),
+ y: avg_times
+ .iter()
+ .zip(iter_counts.iter())
+ .filter_map(
+ |((_, label), i)| {
+ if label.is_outlier() {
+ None
+ } else {
+ Some(i)
+ }
+ },
+ ),
+ },
+ |c| {
+ c.set(DARK_BLUE)
+ .set(Label("\"Clean\" sample"))
+ .set(PointType::FilledCircle)
+ .set(POINT_SIZE)
+ },
+ )
+ .plot(
+ Points {
+ x: avg_times
+ .iter()
+ .zip(scaled_avg_times.iter())
+ .filter_map(
+ |((_, label), t)| {
+ if label.is_mild() {
+ Some(t)
+ } else {
+ None
+ }
+ },
+ ),
+ y: avg_times
+ .iter()
+ .zip(iter_counts.iter())
+ .filter_map(
+ |((_, label), i)| {
+ if label.is_mild() {
+ Some(i)
+ } else {
+ None
+ }
+ },
+ ),
+ },
+ |c| {
+ c.set(DARK_ORANGE)
+ .set(Label("Mild outliers"))
+ .set(POINT_SIZE)
+ .set(PointType::FilledCircle)
+ },
+ )
+ .plot(
+ Points {
+ x: avg_times
+ .iter()
+ .zip(scaled_avg_times.iter())
+ .filter_map(
+ |((_, label), t)| {
+ if label.is_severe() {
+ Some(t)
+ } else {
+ None
+ }
+ },
+ ),
+ y: avg_times
+ .iter()
+ .zip(iter_counts.iter())
+ .filter_map(
+ |((_, label), i)| {
+ if label.is_severe() {
+ Some(i)
+ } else {
+ None
+ }
+ },
+ ),
+ },
+ |c| {
+ c.set(DARK_RED)
+ .set(Label("Severe outliers"))
+ .set(POINT_SIZE)
+ .set(PointType::FilledCircle)
+ },
+ )
+ .plot(
+ Lines {
+ x: &[lomt, lomt],
+ y: vertical,
+ },
+ |c| c.set(DARK_ORANGE).set(LINEWIDTH).set(LineType::Dash),
+ )
+ .plot(
+ Lines {
+ x: &[himt, himt],
+ y: vertical,
+ },
+ |c| c.set(DARK_ORANGE).set(LINEWIDTH).set(LineType::Dash),
+ )
+ .plot(
+ Lines {
+ x: &[lost, lost],
+ y: vertical,
+ },
+ |c| c.set(DARK_RED).set(LINEWIDTH).set(LineType::Dash),
+ )
+ .plot(
+ Lines {
+ x: &[hist, hist],
+ y: vertical,
+ },
+ |c| c.set(DARK_RED).set(LINEWIDTH).set(LineType::Dash),
+ );
+ figure.set(Title(gnuplot_escape(id.as_title())));
+
+ let path = context.report_path(id, "pdf.svg");
+ debug_script(&path, &figure);
+ figure.set(Output(path)).draw().unwrap()
+}
+
+pub(crate) fn pdf_small(
+ id: &BenchmarkId,
+ context: &ReportContext,
+ formatter: &dyn ValueFormatter,
+ measurements: &MeasurementData<'_>,
+ size: Option<Size>,
+) -> Child {
+ let avg_times = &*measurements.avg_times;
+ let typical = avg_times.max();
+ let mut scaled_avg_times: Vec<f64> = (avg_times as &Sample<f64>).iter().cloned().collect();
+ let unit = formatter.scale_values(typical, &mut scaled_avg_times);
+ let scaled_avg_times = Sample::new(&scaled_avg_times);
+ let mean = scaled_avg_times.mean();
+
+ let (xs, ys, mean_y) = kde::sweep_and_estimate(scaled_avg_times, KDE_POINTS, None, mean);
+ let xs_ = Sample::new(&xs);
+ let ys_ = Sample::new(&ys);
+
+ let y_limit = ys_.max() * 1.1;
+ let zeros = iter::repeat(0);
+
+ let mut figure = Figure::new();
+ figure
+ .set(Font(DEFAULT_FONT))
+ .set(size.unwrap_or(SIZE))
+ .configure(Axis::BottomX, |a| {
+ a.set(Label(format!("Average time ({})", unit)))
+ .set(Range::Limits(xs_.min(), xs_.max()))
+ })
+ .configure(Axis::LeftY, |a| {
+ a.set(Label("Density (a.u.)"))
+ .set(Range::Limits(0., y_limit))
+ })
+ .configure(Axis::RightY, |a| a.hide())
+ .configure(Key, |k| k.hide())
+ .plot(
+ FilledCurve {
+ x: &*xs,
+ y1: &*ys,
+ y2: zeros,
+ },
+ |c| {
+ c.set(Axes::BottomXRightY)
+ .set(DARK_BLUE)
+ .set(Label("PDF"))
+ .set(Opacity(0.25))
+ },
+ )
+ .plot(
+ Lines {
+ x: &[mean, mean],
+ y: &[0., mean_y],
+ },
+ |c| c.set(DARK_BLUE).set(LINEWIDTH).set(Label("Mean")),
+ );
+
+ let path = context.report_path(id, "pdf_small.svg");
+ debug_script(&path, &figure);
+ figure.set(Output(path)).draw().unwrap()
+}
+
+fn pdf_comparison_figure(
+ formatter: &dyn ValueFormatter,
+ measurements: &MeasurementData<'_>,
+ comparison: &ComparisonData,
+ size: Option<Size>,
+) -> Figure {
+ let base_avg_times = Sample::new(&comparison.base_avg_times);
+ let typical = base_avg_times.max().max(measurements.avg_times.max());
+ let mut scaled_base_avg_times: Vec<f64> = comparison.base_avg_times.clone();
+ let unit = formatter.scale_values(typical, &mut scaled_base_avg_times);
+ let scaled_base_avg_times = Sample::new(&scaled_base_avg_times);
+
+ let mut scaled_new_avg_times: Vec<f64> = (&measurements.avg_times as &Sample<f64>)
+ .iter()
+ .cloned()
+ .collect();
+ let _ = formatter.scale_values(typical, &mut scaled_new_avg_times);
+ let scaled_new_avg_times = Sample::new(&scaled_new_avg_times);
+
+ let base_mean = scaled_base_avg_times.mean();
+ let new_mean = scaled_new_avg_times.mean();
+
+ let (base_xs, base_ys, base_y_mean) =
+ kde::sweep_and_estimate(scaled_base_avg_times, KDE_POINTS, None, base_mean);
+ let (xs, ys, y_mean) =
+ kde::sweep_and_estimate(scaled_new_avg_times, KDE_POINTS, None, new_mean);
+
+ let zeros = iter::repeat(0);
+
+ let mut figure = Figure::new();
+ figure
+ .set(Font(DEFAULT_FONT))
+ .set(size.unwrap_or(SIZE))
+ .configure(Axis::BottomX, |a| {
+ a.set(Label(format!("Average time ({})", unit)))
+ })
+ .configure(Axis::LeftY, |a| a.set(Label("Density (a.u.)")))
+ .configure(Axis::RightY, |a| a.hide())
+ .configure(Key, |k| {
+ k.set(Justification::Left)
+ .set(Order::SampleText)
+ .set(Position::Outside(Vertical::Top, Horizontal::Right))
+ })
+ .plot(
+ FilledCurve {
+ x: &*base_xs,
+ y1: &*base_ys,
+ y2: zeros.clone(),
+ },
+ |c| c.set(DARK_RED).set(Label("Base PDF")).set(Opacity(0.5)),
+ )
+ .plot(
+ Lines {
+ x: &[base_mean, base_mean],
+ y: &[0., base_y_mean],
+ },
+ |c| c.set(DARK_RED).set(Label("Base Mean")).set(LINEWIDTH),
+ )
+ .plot(
+ FilledCurve {
+ x: &*xs,
+ y1: &*ys,
+ y2: zeros,
+ },
+ |c| c.set(DARK_BLUE).set(Label("New PDF")).set(Opacity(0.5)),
+ )
+ .plot(
+ Lines {
+ x: &[new_mean, new_mean],
+ y: &[0., y_mean],
+ },
+ |c| c.set(DARK_BLUE).set(Label("New Mean")).set(LINEWIDTH),
+ );
+ figure
+}
+
+pub(crate) fn pdf_comparison(
+ id: &BenchmarkId,
+ context: &ReportContext,
+ formatter: &dyn ValueFormatter,
+ measurements: &MeasurementData<'_>,
+ comparison: &ComparisonData,
+ size: Option<Size>,
+) -> Child {
+ let mut figure = pdf_comparison_figure(formatter, measurements, comparison, size);
+ figure.set(Title(gnuplot_escape(id.as_title())));
+ let path = context.report_path(id, "both/pdf.svg");
+ debug_script(&path, &figure);
+ figure.set(Output(path)).draw().unwrap()
+}
+
+pub(crate) fn pdf_comparison_small(
+ id: &BenchmarkId,
+ context: &ReportContext,
+ formatter: &dyn ValueFormatter,
+ measurements: &MeasurementData<'_>,
+ comparison: &ComparisonData,
+ size: Option<Size>,
+) -> Child {
+ let mut figure = pdf_comparison_figure(formatter, measurements, comparison, size);
+ figure.configure(Key, |k| k.hide());
+ let path = context.report_path(id, "relative_pdf_small.svg");
+ debug_script(&path, &figure);
+ figure.set(Output(path)).draw().unwrap()
+}