perf_plotter/
perf.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use std::time::{Duration, Instant};
4
5use plotters::prelude::*;
6
7use crate::PerfPlotterError;
8
9pub fn generate_performance_png<F, E, I>(
10    func: F,
11    mut input_iter: I,
12    output: &std::path::Path,
13    caption: &str,
14) -> Result<(), PerfPlotterError>
15where
16    F: Fn(E) -> Option<()>,
17    I: Iterator<Item = E>,
18{
19    let mut perf_data: Vec<Duration> = Vec::new();
20    let mut i = 0usize;
21    while let Some(input) = input_iter.next() {
22        i += 1;
23        let now = Instant::now();
24        if func(input).is_some() {
25            let elapsed = now.elapsed();
26            println!("Round {}: {} secs", i, elapsed.as_secs_f32());
27            perf_data.push(elapsed);
28        } else {
29            break;
30        }
31    }
32
33    gen_png(&perf_data, output, caption)
34}
35
36fn gen_png(
37    data: &[Duration],
38    output: &std::path::Path,
39    caption: &str,
40) -> Result<(), PerfPlotterError> {
41    let root = BitMapBackend::new(output, (1920, 1080)).into_drawing_area();
42    root.fill(&WHITE)
43        .map_err(|e| PerfPlotterError::new(e.to_string()))?;
44    if data.is_empty() {
45        return Err(PerfPlotterError::new("Empty performance data".into()));
46    }
47
48    // Save to unwrap, we already checked the data is not empty;
49    let min_dur = data.iter().min().unwrap();
50    let max_dur = data.iter().max().unwrap();
51
52    let mut chart = ChartBuilder::on(&root)
53        .caption(caption, ("sans-serif", 50).into_font())
54        .margin(20)
55        .x_label_area_size(30)
56        .y_label_area_size(30)
57        .build_cartesian_2d(
58            1.0f32..(data.len() as f32 + 1.0f32),
59            min_dur.as_secs_f32()..max_dur.as_secs_f32(),
60        )
61        .map_err(|e| PerfPlotterError::new(e.to_string()))?;
62
63    chart
64        .configure_mesh()
65        .x_desc("Round")
66        .y_desc("Sec")
67        .axis_desc_style(("sans-serif", 15))
68        .draw()
69        .map_err(|e| PerfPlotterError::new(e.to_string()))?;
70
71    chart
72        .draw_series(
73            LineSeries::new(
74                data.iter()
75                    .enumerate()
76                    .map(|(i, d)| (i as f32 + 1.0f32, d.as_secs_f32())),
77                RED,
78            )
79            .point_size(2),
80        )
81        .map_err(|e| PerfPlotterError::new(e.to_string()))?;
82
83    Ok(())
84}