pariter/
profile.rs

1mod simple;
2
3pub use simple::{TotalTimeProfiler, TotalTimeStats};
4
5/// An interface to profile iterator consumption/prodution performance
6///
7/// In real applications utilizing pipelining it's important
8/// to measure the production and consumption rates of each
9/// pipeline step, to help identify current bottleneck.
10///
11/// [`ProfileEgress`] and [`ProfileIngress`] pinky-promise to
12///  alway call `start` first, and then call corresponding `end`
13///  before calling `start` again. The final `end` is not guaranteed
14///  to be called (eg. if the inner iterator panicked), and in
15///  particular the [`ProfileEgress`] will usually call the last
16///  `start` with no-corresponding `end`, since it's impossible to
17///  predict if the next iterator chain step will call `next()`
18///  again to pull for the next item.
19///
20///  See [`TotalTimeProfiler`] for simple starting built-in implementation.
21pub trait Profiler {
22    fn start(&mut self);
23    fn end(&mut self);
24}
25
26/// Profiles the time spent waiting for the downstream
27/// iterator step to consume the previous returned item
28/// and ask for the next one (or in other words, the time
29/// spent NOT spent waiting on `next()` of the inner iterator.
30pub struct ProfileEgress<I, P> {
31    inner: I,
32    profiler: P,
33    first_returned: bool,
34}
35
36/// Profiles the time spent waiting for the upstream
37/// iterator step to produce the next returned item
38/// (or in other words, the time it took to call `next()`).
39pub struct ProfileIngress<I, P> {
40    inner: I,
41    profiler: P,
42}
43
44impl<I, P> ProfileEgress<I, P> {
45    pub fn new(inner: I, profiler: P) -> Self {
46        Self {
47            inner,
48            profiler,
49            first_returned: false,
50        }
51    }
52}
53
54impl<I, P> ProfileIngress<I, P> {
55    pub fn new(inner: I, profiler: P) -> Self {
56        Self { inner, profiler }
57    }
58}
59
60impl<I, P> Iterator for ProfileEgress<I, P>
61where
62    I: Iterator,
63    P: Profiler,
64{
65    type Item = I::Item;
66
67    fn next(&mut self) -> Option<Self::Item> {
68        if self.first_returned {
69            self.profiler.end();
70        } else {
71            // might as well switch it before actually pulling
72            self.first_returned = true;
73        }
74
75        let item = self.inner.next();
76
77        self.profiler.start();
78
79        item
80    }
81}
82
83impl<I, P> Iterator for ProfileIngress<I, P>
84where
85    I: Iterator,
86    P: Profiler,
87{
88    type Item = I::Item;
89
90    fn next(&mut self) -> Option<Self::Item> {
91        self.profiler.start();
92
93        let item = self.inner.next();
94
95        self.profiler.end();
96        item
97    }
98}