git_perf/
measurement_retrieval.rs

1use crate::{
2    data::{CommitSummary, MeasurementData, MeasurementSummary, ReductionFunc},
3    git_interop::{self},
4    stats::NumericReductionFunc,
5};
6
7use anyhow::Result;
8
9// TODO(kaihowl) oh god naming
10pub trait ReductionFuncIterator<'a>: Iterator<Item = &'a MeasurementData> {
11    fn reduce_by(self, fun: ReductionFunc) -> Option<MeasurementSummary>;
12}
13
14pub fn summarize_measurements<'a, F>(
15    commits: impl Iterator<Item = Result<Commit>> + 'a,
16    summarize_by: &'a ReductionFunc,
17    filter_by: &'a F,
18) -> impl Iterator<Item = Result<CommitSummary>> + 'a
19where
20    F: Fn(&MeasurementData) -> bool,
21{
22    let measurements = commits.map(move |c| {
23        c.map(|c| {
24            let measurement = c
25                .measurements
26                .iter()
27                .filter(|m| filter_by(m))
28                .reduce_by(*summarize_by);
29
30            CommitSummary {
31                commit: c.commit,
32                measurement,
33            }
34        })
35    });
36
37    let mut first_epoch = None;
38
39    // TODO(kaihowl) this is a second repsonsibility, move out? "EpochClearing"
40    measurements.take_while(move |m| match &m {
41        Ok(CommitSummary {
42            measurement: Some(m),
43            ..
44        }) => {
45            let prev_epoch = first_epoch;
46            first_epoch = Some(m.epoch);
47            prev_epoch.unwrap_or(m.epoch) == m.epoch
48        }
49        _ => true,
50    })
51}
52
53impl<'a, T> ReductionFuncIterator<'a> for T
54where
55    T: Iterator<Item = &'a MeasurementData>,
56{
57    fn reduce_by(self, fun: ReductionFunc) -> Option<MeasurementSummary> {
58        let mut peekable = self.peekable();
59        let expected_epoch = peekable.peek().map(|m| m.epoch);
60        let mut vals = peekable.map(|m| {
61            debug_assert_eq!(Some(m.epoch), expected_epoch);
62            m.val
63        });
64
65        let aggregate_val = vals.aggregate_by(fun);
66
67        Some(MeasurementSummary {
68            epoch: expected_epoch?,
69            val: aggregate_val?,
70        })
71    }
72}
73
74#[derive(Debug, PartialEq)]
75pub struct Commit {
76    pub commit: String,
77    pub measurements: Vec<MeasurementData>,
78}
79
80// TODO(hoewelmk) copies all measurements, expensive...
81// TODO(kaihowl) missing check for shallow clone marker!
82pub fn walk_commits(num_commits: usize) -> Result<impl Iterator<Item = Result<Commit>>> {
83    let vec = git_interop::walk_commits(num_commits)?;
84    Ok(vec
85        .into_iter()
86        .take(num_commits)
87        .map(|(commit_id, lines)| -> Result<Commit> {
88            let measurements = crate::serialization::deserialize(&lines.join("\n"));
89            Ok(Commit {
90                commit: commit_id,
91                measurements,
92            })
93        }))
94    // When this fails it is due to a shallow clone.
95    // TODO(kaihowl) proper shallow clone support
96    // https://github.com/libgit2/libgit2/issues/3058 tracks that we fail to revwalk the
97    // last commit because the parent cannot be loooked up.
98}