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}