evobench_tools/evaluator/
options.rs

1//! Options parameterizing the evaluation (excludes subcommands or
2//! similar, those remain in src/bin/*.rs).
3
4use std::path::PathBuf;
5
6use anyhow::{Result, bail};
7
8use crate::stats_tables::stats::StatsField;
9
10// We use 101 buckets for percentiles instead of 100, so that we get
11// buckets at positions 50, 25, 75 for exact matches, OK? (Although
12// note that the `Stats` median is not based on those buckets
13// (anymore).)
14pub const TILE_COUNT: usize = 101;
15
16#[derive(clap::Args, Debug)]
17pub struct EvaluationOpts {
18    /// The width of the column with the probes path, in characters
19    /// (as per Excel's definition of characters)
20    // (This is for Excel output only; could there be a better place?
21    // But the `OutputOpts` are just options on the same level (via
22    // flatten), currently, only subcommands would allow selective
23    // choice, and there may be no way to specify multiple subcommands
24    // in the same command invocation.)
25    #[clap(short, long, default_value = "100")]
26    pub key_width: f64,
27
28    /// Include the internally-allocated thread number in call
29    /// path strings in the output.
30    #[clap(short, long)]
31    pub show_thread_number: bool,
32
33    /// Show the call path so that the leaf instead of the root is on
34    /// the left (only has an effect on tables (Excel), not
35    /// flamegraphs).
36    #[clap(short = 'r', long)]
37    pub show_reversed: bool,
38}
39
40/// Using private fields, to enforce calling .check()!
41#[derive(clap::Args, Debug)]
42pub struct OutputOpts {
43    /// Path to write Excel output to
44    #[clap(short, long)]
45    excel: Option<PathBuf>,
46
47    /// Base path to write flame graph SVG to; "-$type.svg" is
48    /// appended, where type is "real", "cpu", "sys" or
49    /// "ctx-switches".
50    #[clap(short, long)]
51    flame: Option<PathBuf>,
52}
53
54/// Do not use for level 0 (i.e. `single` subcommand), there sum must
55/// always be used!
56#[derive(clap::Args, Debug)]
57pub struct FlameFieldOpt {
58    /// What field to select from the last stage for the flame graph
59    /// (tables show all stats values, but flame graphs can only show
60    /// one, that's why this option is needed for those).
61    #[clap(long, default_value = "avg")]
62    pub flame_field: StatsField<TILE_COUNT>,
63}
64
65/// OutputOpts split into checked `OutputVariants` and possibly other
66/// options (flame_field was here in the past, not any more, but maybe
67/// other fields will come in the future, thus keeping the separation
68/// into OutputVariants and the wrapper)
69pub struct CheckedOutputOptions {
70    pub variants: OutputVariants<PathBuf>,
71}
72
73impl OutputOpts {
74    pub fn check(self) -> Result<CheckedOutputOptions> {
75        let Self { excel, flame } = self;
76
77        let any_given = [excel.is_some(), flame.is_some()].iter().any(|b| *b);
78        if !any_given {
79            bail!("no output files were specified")
80        }
81
82        Ok(CheckedOutputOptions {
83            variants: OutputVariants { excel, flame },
84        })
85    }
86}
87
88/// Same as OutputOpts but at least one file is set; parameterized so
89/// it can be used for pipelining via its `map` method.
90#[derive(Clone)]
91pub struct OutputVariants<T> {
92    pub excel: Option<T>,
93    pub flame: Option<T>,
94}
95
96#[derive(Clone, Copy, PartialEq)]
97pub enum CheckedOutputOptionsMapCase {
98    Excel,
99    Flame,
100}
101
102impl<T> OutputVariants<T> {
103    /// get a field
104    pub fn get(&self, case: CheckedOutputOptionsMapCase) -> &Option<T> {
105        match case {
106            CheckedOutputOptionsMapCase::Excel => &self.excel,
107            CheckedOutputOptionsMapCase::Flame => &self.flame,
108        }
109    }
110
111    /// `f` is applied to all fields that are `Some`
112    pub fn map<U>(self, f: impl Fn(CheckedOutputOptionsMapCase, T) -> U) -> OutputVariants<U> {
113        let Self { excel, flame } = self;
114        OutputVariants {
115            excel: excel.map(|v| f(CheckedOutputOptionsMapCase::Excel, v)),
116            flame: flame.map(|v| f(CheckedOutputOptionsMapCase::Flame, v)),
117        }
118    }
119
120    /// `f` is applied to all fields that are `Some`
121    pub fn try_map<U, E>(
122        self,
123        f: impl Fn(CheckedOutputOptionsMapCase, T) -> Result<U, E>,
124    ) -> Result<OutputVariants<U>, E> {
125        let Self { excel, flame } = self;
126        Ok(OutputVariants {
127            excel: excel
128                .map(|v| f(CheckedOutputOptionsMapCase::Excel, v))
129                .transpose()?,
130            flame: flame
131                .map(|v| f(CheckedOutputOptionsMapCase::Flame, v))
132                .transpose()?,
133        })
134    }
135}
136
137#[derive(clap::Args, Debug)]
138pub struct EvaluationAndOutputOpts {
139    #[clap(flatten)]
140    pub evaluation_opts: EvaluationOpts,
141    #[clap(flatten)]
142    pub output_opts: OutputOpts,
143}
144
145#[derive(clap::Args, Debug)]
146pub struct FieldSelectorDimension3Opt {
147    /// What stats field to select for the summary stats (i.e. of the
148    /// 2nd dimension, for calculating the 3rd dimension in the data
149    /// evaluation, after dimensions 1 (probe name) and 2 (stats
150    /// fields)). Valid values: n|sum|average|median|sd or a floating
151    /// point number between 0 and 1 for selecting a percentile.
152    #[clap(long, default_value = "avg")]
153    pub summary_field: StatsField<TILE_COUNT>,
154}
155
156#[derive(clap::Args, Debug)]
157pub struct FieldSelectorDimension4Opt {
158    /// What stats field to select for the trend stats (i.e. of the
159    /// 3rd dimension, for calculating the 4nd dimension in the data
160    /// evaluation, after dimensions 1 (probe name), 2 (stats fields),
161    /// 3 (stats of the field from dimension 2 selected by the
162    /// --summary-field option)). See --summary-field docs for the
163    /// valid values.
164    #[clap(long, default_value = "median")]
165    pub trend_field: StatsField<TILE_COUNT>,
166}