git_perf/
measurement_retrieval.rs

1use crate::{
2    data::{CommitSummary, MeasurementData, MeasurementSummary},
3    git::git_interop::{self},
4    stats::NumericReductionFunc,
5};
6
7use git_perf_cli_types::ReductionFunc;
8
9use anyhow::Result;
10
11pub trait MeasurementReducer<'a>: Iterator<Item = &'a MeasurementData> {
12    fn reduce_by(self, fun: ReductionFunc) -> Option<MeasurementSummary>;
13}
14
15pub fn summarize_measurements<'a, F>(
16    commits: impl Iterator<Item = Result<Commit>> + 'a,
17    summarize_by: &'a ReductionFunc,
18    filter_by: &'a F,
19) -> impl Iterator<Item = Result<CommitSummary>> + 'a
20where
21    F: Fn(&MeasurementData) -> bool,
22{
23    commits.map(move |c| {
24        c.map(|c| {
25            let measurement = c
26                .measurements
27                .iter()
28                .filter(|m| filter_by(m))
29                .reduce_by(*summarize_by);
30
31            CommitSummary {
32                commit: c.commit,
33                measurement,
34            }
35        })
36    })
37}
38
39/// Adapter to take results while the epoch is the same as the first one encountered.
40pub fn take_while_same_epoch<I>(iter: I) -> impl Iterator<Item = Result<CommitSummary>>
41where
42    I: Iterator<Item = Result<CommitSummary>>,
43{
44    let mut first_epoch: Option<u32> = None;
45    iter.take_while(move |m| match m {
46        Ok(CommitSummary {
47            measurement: Some(m),
48            ..
49        }) => {
50            let prev_epoch = first_epoch;
51            first_epoch = Some(m.epoch);
52            prev_epoch.unwrap_or(m.epoch) == m.epoch
53        }
54        _ => true,
55    })
56}
57
58impl<'a, T> MeasurementReducer<'a> for T
59where
60    T: Iterator<Item = &'a MeasurementData>,
61{
62    fn reduce_by(self, fun: ReductionFunc) -> Option<MeasurementSummary> {
63        let mut peekable = self.peekable();
64        let expected_epoch = peekable.peek().map(|m| m.epoch);
65        let mut vals = peekable.map(|m| {
66            debug_assert_eq!(Some(m.epoch), expected_epoch);
67            m.val
68        });
69
70        let aggregate_val = vals.aggregate_by(fun);
71
72        Some(MeasurementSummary {
73            epoch: expected_epoch?,
74            val: aggregate_val?,
75        })
76    }
77}
78
79#[derive(Debug, PartialEq)]
80pub struct Commit {
81    pub commit: String,
82    pub measurements: Vec<MeasurementData>,
83}
84
85// Copying all measurements is currently necessary due to deserialization from git notes.
86// Optimizing this would require a change in the storage or deserialization model.
87pub fn walk_commits(num_commits: usize) -> Result<impl Iterator<Item = Result<Commit>>> {
88    let vec = git_interop::walk_commits(num_commits)?;
89    Ok(vec
90        .into_iter()
91        .take(num_commits)
92        .map(|(commit_id, lines)| -> Result<Commit> {
93            let measurements = crate::serialization::deserialize(&lines.join("\n"));
94            Ok(Commit {
95                commit: commit_id,
96                measurements,
97            })
98        }))
99    // When this fails it is due to a shallow clone.
100}