Skip to content

Commit 64758f7

Browse files
h33pberkus
authored andcommitted
Adds ability to plot throughput on summary page.
Fixes #149.
1 parent a0a4f75 commit 64758f7

7 files changed

Lines changed: 144 additions & 37 deletions

File tree

src/html/mod.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::estimate::Estimate;
55
use crate::format;
66
use crate::fs;
77
use crate::measurement::ValueFormatter;
8-
use crate::plot::{PlotContext, PlotData, Plotter};
8+
use crate::plot::{LinePlotConfig, PlotContext, PlotData, Plotter};
99
use crate::SavedSample;
1010
use criterion_plot::Size;
1111
use serde::Serialize;
@@ -84,6 +84,7 @@ struct SummaryContext {
8484

8585
violin_plot: Option<String>,
8686
line_chart: Option<String>,
87+
line_throughput_chart: Option<String>,
8788

8889
benchmarks: Vec<IndividualBenchmark>,
8990
}
@@ -756,15 +757,32 @@ impl Html {
756757

757758
let value_types: Vec<_> = data.iter().map(|&&(id, _)| id.value_type()).collect();
758759
let mut line_path = None;
760+
let mut line_throughput_path = None;
759761

760762
if value_types.iter().all(|x| x == &value_types[0]) {
761763
if let Some(value_type) = value_types[0] {
762764
let values: Vec<_> = data.iter().map(|&&(id, _)| id.as_number()).collect();
763765
if values.iter().any(|x| x != &values[0]) {
764-
self.plotter
765-
.borrow_mut()
766-
.line_comparison(plot_ctx, formatter, data, value_type);
766+
self.plotter.borrow_mut().line_comparison(
767+
LinePlotConfig::time(),
768+
plot_ctx,
769+
formatter,
770+
data,
771+
value_type,
772+
);
767773
line_path = Some(plot_ctx.line_comparison_path());
774+
775+
// value_types being all equal implies throughput types being all equal
776+
if data[0].0.throughput.is_some() {
777+
self.plotter.borrow_mut().line_comparison(
778+
LinePlotConfig::throughput(),
779+
plot_ctx,
780+
formatter,
781+
data,
782+
value_type,
783+
);
784+
line_throughput_path = Some(plot_ctx.line_throughput_comparison_path());
785+
}
768786
}
769787
}
770788
}
@@ -785,6 +803,7 @@ impl Html {
785803

786804
violin_plot: Some(plot_ctx.violin_path().to_string_lossy().into_owned()),
787805
line_chart: line_path.map(|p| p.to_string_lossy().into_owned()),
806+
line_throughput_chart: line_throughput_path.map(|p| p.to_string_lossy().into_owned()),
788807

789808
benchmarks,
790809
};

src/html/summary_report.html.tt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@
6666
<img src="lines.svg" alt="Line Chart" />
6767
<p>This chart shows the mean measured time for each function as the input (or the size of the input) increases.</p>
6868
{{- endif }}
69+
{{- if line_throughput_chart }}
70+
<h3>Throughput Chart</h3>
71+
<img src="lines_throughput.svg" alt="Line Chart" />
72+
<p>This chart shows the mean measured throughput for each function as the input (or the size of the input) increases.</p>
73+
{{- endif }}
6974
{{- for bench in benchmarks }}
7075
<section class="plots">
7176
<a href="{bench.path}/report/index.html">
@@ -106,4 +111,4 @@
106111
</div>
107112
</body>
108113

109-
</html>
114+
</html>

src/plot/gnuplot_backend/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::measurement::ValueFormatter;
2222
use crate::report::{BenchmarkId, ValueType};
2323
use crate::stats::bivariate::Data;
2424

25-
use super::{PlotContext, PlotData, Plotter};
25+
use super::{LinePlotConfig, PlotContext, PlotData, Plotter};
2626
use crate::format;
2727

2828
fn gnuplot_escape(string: &str) -> String {
@@ -187,13 +187,15 @@ impl Plotter for Gnuplot {
187187

188188
fn line_comparison(
189189
&mut self,
190+
line_config: LinePlotConfig,
190191
ctx: PlotContext<'_>,
191192
formatter: &dyn ValueFormatter,
192193
all_curves: &[&(&BenchmarkId, Vec<f64>)],
193194
value_type: ValueType,
194195
) {
195-
let path = ctx.line_comparison_path();
196+
let path = (line_config.path)(&ctx);
196197
self.process_list.push(line_comparison(
198+
line_config,
197199
formatter,
198200
ctx.id.as_title(),
199201
all_curves,

src/plot/gnuplot_backend/summary.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::{debug_script, gnuplot_escape};
22
use super::{DARK_BLUE, DEFAULT_FONT, KDE_POINTS, LINEWIDTH, POINT_SIZE, SIZE};
33
use crate::kde;
44
use crate::measurement::ValueFormatter;
5+
use crate::plot::LinePlotConfig;
56
use crate::report::{BenchmarkId, ValueType};
67
use crate::stats::univariate::Sample;
78
use crate::AxisScale;
@@ -33,7 +34,8 @@ impl AxisScale {
3334
}
3435

3536
#[allow(clippy::explicit_counter_loop)]
36-
pub fn line_comparison(
37+
pub(crate) fn line_comparison(
38+
line_cfg: LinePlotConfig,
3739
formatter: &dyn ValueFormatter,
3840
title: &str,
3941
all_curves: &[&(&BenchmarkId, Vec<f64>)],
@@ -66,18 +68,22 @@ pub fn line_comparison(
6668

6769
let mut i = 0;
6870

69-
let max = all_curves
71+
let (max_id, max) = all_curves
7072
.iter()
71-
.map(|&(_, data)| Sample::new(data).mean())
72-
.fold(f64::NAN, f64::max);
73+
.map(|&(id, data)| (*id, Sample::new(data).mean()))
74+
.fold(None, |prev: Option<(&BenchmarkId, f64)>, next| match prev {
75+
Some(prev) if prev.1 >= next.1 => Some(prev),
76+
_ => Some(next),
77+
})
78+
.unwrap();
7379

74-
let mut dummy = [1.0];
75-
let unit = formatter.scale_values(max, &mut dummy);
80+
let mut max_formatted = [max];
81+
let unit = (line_cfg.scale)(formatter, max_id, max, max_id, &mut max_formatted);
7682

7783
f.configure(Axis::LeftY, |a| {
7884
a.configure(Grid::Major, |g| g.show())
7985
.configure(Grid::Minor, |g| g.hide())
80-
.set(Label(format!("Average time ({})", unit)))
86+
.set(Label(format!("Average {} ({})", line_cfg.label, unit)))
8187
.set(axis_scale.to_gnuplot())
8288
});
8389

@@ -90,14 +96,15 @@ pub fn line_comparison(
9096
// Unwrap is fine here because it will only fail if the assumptions above are not true
9197
// ie. programmer error.
9298
let x = id.as_number().unwrap();
93-
let y = Sample::new(sample).mean();
99+
let mut y = [Sample::new(sample).mean()];
100+
101+
(line_cfg.scale)(formatter, max_id, max, id, &mut y);
94102

95-
(x, y)
103+
(x, y[0])
96104
})
97105
.collect();
98106
tuples.sort_by(|&(ax, _), &(bx, _)| (ax.partial_cmp(&bx).unwrap_or(Ordering::Less)));
99-
let (xs, mut ys): (Vec<_>, Vec<_>) = tuples.into_iter().unzip();
100-
formatter.scale_values(max, &mut ys);
107+
let (xs, ys): (Vec<_>, Vec<_>) = tuples.into_iter().unzip();
101108

102109
let function_name = key.as_ref().map(|string| gnuplot_escape(string));
103110

src/plot/mod.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub(crate) use plotters_backend::PlottersBackend;
99
use crate::estimate::Statistic;
1010
use crate::measurement::ValueFormatter;
1111
use crate::report::{BenchmarkId, ComparisonData, MeasurementData, ReportContext, ValueType};
12+
use crate::Throughput;
1213
use std::path::PathBuf;
1314

1415
const REPORT_STATS: [Statistic; 7] = [
@@ -50,6 +51,14 @@ impl<'a> PlotContext<'a> {
5051
path
5152
}
5253

54+
pub fn line_throughput_comparison_path(&self) -> PathBuf {
55+
let mut path = self.context.output_directory.clone();
56+
path.push(self.id.as_directory_name());
57+
path.push("report");
58+
path.push("lines_throughput.svg");
59+
path
60+
}
61+
5362
pub fn violin_path(&self) -> PathBuf {
5463
let mut path = self.context.output_directory.clone();
5564
path.push(self.id.as_directory_name());
@@ -73,6 +82,53 @@ impl<'a> PlotData<'a> {
7382
}
7483
}
7584

85+
#[derive(Clone, Copy)]
86+
pub(crate) struct LinePlotConfig {
87+
label: &'static str,
88+
scale: fn(&dyn ValueFormatter, &BenchmarkId, f64, &BenchmarkId, &mut [f64]) -> &'static str,
89+
path: fn(&PlotContext<'_>) -> PathBuf,
90+
}
91+
92+
impl LinePlotConfig {
93+
pub fn time() -> Self {
94+
Self {
95+
label: "time",
96+
scale: |formatter, _, max, _, vals| formatter.scale_values(max, vals),
97+
path: |ctx| ctx.line_comparison_path(),
98+
}
99+
}
100+
101+
pub fn throughput() -> Self {
102+
Self {
103+
label: "throughput",
104+
scale: |formatter, max_id, max, id, vals| {
105+
// Scale values to be in line with max_id throughput
106+
let from = id
107+
.throughput
108+
.as_ref()
109+
.expect("Throughput chart expects throughput to be defined");
110+
let to = max_id.throughput.as_ref().unwrap();
111+
112+
let (from_bytes, to_bytes) = match (from, to) {
113+
(Throughput::Bytes(from), Throughput::Bytes(to)) => (from, to),
114+
(Throughput::BytesDecimal(from), Throughput::BytesDecimal(to)) => (from, to),
115+
(Throughput::Elements(from), Throughput::Elements(to)) => (from, to),
116+
_ => unreachable!("throughput types expected to be equal"),
117+
};
118+
119+
let mul = *to_bytes as f64 / *from_bytes as f64;
120+
121+
for val in vals.iter_mut() {
122+
*val *= mul;
123+
}
124+
125+
formatter.scale_throughputs(max, to, vals)
126+
},
127+
path: |ctx| ctx.line_throughput_comparison_path(),
128+
}
129+
}
130+
}
131+
76132
pub(crate) trait Plotter {
77133
fn pdf(&mut self, ctx: PlotContext<'_>, data: PlotData<'_>);
78134

@@ -86,6 +142,7 @@ pub(crate) trait Plotter {
86142

87143
fn line_comparison(
88144
&mut self,
145+
line_config: LinePlotConfig,
89146
ctx: PlotContext<'_>,
90147
formatter: &dyn ValueFormatter,
91148
all_curves: &[&(&BenchmarkId, Vec<f64>)],

src/plot/plotters_backend/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{PlotContext, PlotData, Plotter};
1+
use super::{LinePlotConfig, PlotContext, PlotData, Plotter};
22
use crate::measurement::ValueFormatter;
33
use crate::report::{BenchmarkId, ComparisonData, MeasurementData, ValueType};
44
use plotters::data::float::pretty_print_float;
@@ -184,13 +184,15 @@ impl Plotter for PlottersBackend {
184184

185185
fn line_comparison(
186186
&mut self,
187+
line_config: LinePlotConfig,
187188
ctx: PlotContext<'_>,
188189
formatter: &dyn ValueFormatter,
189190
all_curves: &[&(&BenchmarkId, Vec<f64>)],
190191
value_type: ValueType,
191192
) {
192-
let path = ctx.line_comparison_path();
193+
let path = (line_config.path)(&ctx);
193194
summary::line_comparison(
195+
line_config,
194196
formatter,
195197
ctx.id.as_title(),
196198
all_curves,

src/plot/plotters_backend/summary.rs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@ static COMPARISON_COLORS: [RGBColor; NUM_COLORS] = [
2020
RGBColor(0, 255, 127),
2121
];
2222

23-
pub fn line_comparison(
23+
pub(crate) fn line_comparison(
24+
line_cfg: LinePlotConfig,
2425
formatter: &dyn ValueFormatter,
2526
title: &str,
2627
all_curves: &[&(&BenchmarkId, Vec<f64>)],
2728
path: &Path,
2829
value_type: ValueType,
2930
axis_scale: AxisScale,
3031
) {
31-
let (unit, series_data) = line_comparison_series_data(formatter, all_curves);
32+
let (unit, series_data) = line_comparison_series_data(line_cfg, formatter, all_curves);
3233

3334
let x_range =
3435
plotters::data::fitting_range(series_data.iter().flat_map(|(_, xs, _)| xs.iter()));
@@ -40,10 +41,17 @@ pub fn line_comparison(
4041
.unwrap();
4142

4243
match axis_scale {
43-
AxisScale::Linear => {
44-
draw_line_comarision_figure(root_area, unit, x_range, y_range, value_type, series_data);
45-
}
46-
AxisScale::Logarithmic => draw_line_comarision_figure(
44+
AxisScale::Linear => draw_line_comparison_figure(
45+
line_cfg,
46+
root_area,
47+
unit,
48+
x_range,
49+
y_range,
50+
value_type,
51+
series_data,
52+
),
53+
AxisScale::Logarithmic => draw_line_comparison_figure(
54+
line_cfg,
4755
root_area,
4856
unit,
4957
x_range.log_scale(),
@@ -54,7 +62,8 @@ pub fn line_comparison(
5462
}
5563
}
5664

57-
fn draw_line_comarision_figure<XR: AsRangedCoord<Value = f64>, YR: AsRangedCoord<Value = f64>>(
65+
fn draw_line_comparison_figure<XR: AsRangedCoord<Value = f64>, YR: AsRangedCoord<Value = f64>>(
66+
line_cfg: LinePlotConfig,
5867
root_area: DrawingArea<SVGBackend, Shift>,
5968
y_unit: &str,
6069
x_range: XR,
@@ -83,7 +92,7 @@ fn draw_line_comarision_figure<XR: AsRangedCoord<Value = f64>, YR: AsRangedCoord
8392
.configure_mesh()
8493
.disable_mesh()
8594
.x_desc(format!("Input{}", input_suffix))
86-
.y_desc(format!("Average time ({})", y_unit))
95+
.y_desc(format!("Average {} ({})", line_cfg.label, y_unit))
8796
.draw()
8897
.unwrap();
8998

@@ -116,16 +125,21 @@ fn draw_line_comarision_figure<XR: AsRangedCoord<Value = f64>, YR: AsRangedCoord
116125

117126
#[allow(clippy::type_complexity)]
118127
fn line_comparison_series_data<'a>(
128+
line_cfg: LinePlotConfig,
119129
formatter: &dyn ValueFormatter,
120130
all_curves: &[&(&'a BenchmarkId, Vec<f64>)],
121131
) -> (&'static str, Vec<(Option<&'a String>, Vec<f64>, Vec<f64>)>) {
122-
let max = all_curves
132+
let (max_id, max) = all_curves
123133
.iter()
124-
.map(|&(_, data)| Sample::new(data).mean())
125-
.fold(f64::NAN, f64::max);
134+
.map(|&(id, data)| (*id, Sample::new(data).mean()))
135+
.fold(None, |prev: Option<(&BenchmarkId, f64)>, next| match prev {
136+
Some(prev) if prev.1 >= next.1 => Some(prev),
137+
_ => Some(next),
138+
})
139+
.unwrap();
126140

127-
let mut dummy = [1.0];
128-
let unit = formatter.scale_values(max, &mut dummy);
141+
let mut max_formatted = [max];
142+
let unit = (line_cfg.scale)(formatter, max_id, max, max_id, &mut max_formatted);
129143

130144
let mut series_data = vec![];
131145

@@ -138,15 +152,16 @@ fn line_comparison_series_data<'a>(
138152
// Unwrap is fine here because it will only fail if the assumptions above are not true
139153
// ie. programmer error.
140154
let x = id.as_number().unwrap();
141-
let y = Sample::new(sample).mean();
155+
let mut y = [Sample::new(sample).mean()];
156+
157+
(line_cfg.scale)(formatter, max_id, max, id, &mut y);
142158

143-
(x, y)
159+
(x, y[0])
144160
})
145161
.collect();
146162
tuples.sort_by(|&(ax, _), &(bx, _)| (ax.partial_cmp(&bx).unwrap_or(Ordering::Less)));
147163
let function_name = key.as_ref();
148-
let (xs, mut ys): (Vec<_>, Vec<_>) = tuples.into_iter().unzip();
149-
formatter.scale_values(max, &mut ys);
164+
let (xs, ys): (Vec<_>, Vec<_>) = tuples.into_iter().unzip();
150165
series_data.push((function_name, xs, ys));
151166
}
152167
(unit, series_data)

0 commit comments

Comments
 (0)