rustics/
time.rs

1//
2//  Copyright 2024 Jonathan L Bertoni
3//
4//  This code is available under the Berkeley 2-Clause, Berkeley 3-clause,
5//  and MIT licenses.
6//
7
8//!
9//! ## Types
10//!
11//! * Timer
12//!   * The Timer trait defines the interface for time operations needed by the
13//!     statistics functions.
14//!
15//! * DurationTimer
16//!   * DurationTimer provides a Timer interface to the standard rust Duration type,
17//!     which measures wall-clock time.
18//!
19//! * SimpleClock
20//!   * The SimpleClock trait is an abstraction that can be used to implement
21//!     platform-specific Timer instances.  Something like a cycle counter would
22//!     be an example of a timer that can be used as a SimpleClock.
23//!
24//! * ClockTimer
25//!   * Clock timer is an implementation of Timer that uses a
26//!     SimpleClock underneath.  It is a standard bridge from
27//!     implementation-specific timers to the Rustics types.
28//!
29//! ## Example
30//!```
31//!     use rustics::time::SimpleClock;
32//!
33//!     // This is an example implementation of the SimpleClock trait.  It
34//!     // simply returns a series of time values increasing by one tick
35//!     // per invocation.
36//!
37//!     struct ExampleClock {
38//!         current_time: u128,
39//!         hz:           u128,
40//!     }
41//!
42//!     impl ExampleClock {
43//!         fn new(start_time: u128, hz: u128) -> ExampleClock {
44//!             let current_time = start_time;
45//!
46//!             ExampleClock { current_time, hz }
47//!         }
48//!     }
49//!
50//!     impl SimpleClock for ExampleClock {
51//!         fn get_time(&mut self) -> u128 {
52//!             self.current_time += 1;
53//!             self.current_time
54//!         }
55//!
56//!         fn hz(&self) -> u128 {
57//!             self.hz
58//!         }
59//!     }
60//!
61//!     let     start_time  = 1;
62//!     let     hz          = 1_000_000_000;
63//!     let mut clock       = ExampleClock::new(start_time, hz);
64//!
65//!     // Get a few time values.
66//!
67//!     for i in 1..100 {
68//!         let time = clock.get_time();
69//!
70//!         assert!(time == start_time + i);
71//!     }
72//!
73//!     assert!(clock.hz() == hz);
74//!```
75
76use std::time::Instant;
77use std::rc::Rc;
78use std::cell::RefCell;
79
80use crate::TimerBox;
81use crate::timer_box;
82
83/// A Timer is an abstraction of a clock to be used for performance
84/// monitoring.  It is intended to allow for many implementations.
85/// The underlying clock implementation determines the meaning of an
86/// interval value.  For example, a DurationTimer uses the standard
87/// rust Duration type, which returns wall-clock time.
88
89pub trait Timer {
90    /// The start() method starts a timing interval.  It may be called
91    /// multiple times on a single instance.  The last invocation of
92    /// the start method overrides any previous calls.
93
94    fn start(&mut self);            // start or restart a timer
95
96    /// The finish() method is used at the end of a sample interval.
97    /// It returns the interval time in ticks and also starts a new
98    /// interval, since the restart cost is nearly zero.  Thus, finish()
99    /// can be called multiple times after a start() invocation to return
100    /// the times for a sequence of events.  If a more precise timing is
101    /// required, the user must invoke start().
102
103    fn finish(&mut self) -> i64;    // get the elapsed time and set a new start time
104
105    /// hz() returns the frequency of the underlying clock.
106
107    fn hz(&self) -> u128;           // get the clock hz
108}
109
110/// DurationTimer uses the Rust standard time struct Duration to
111/// measure time intervals.  This timer thus returns wall-clock time.
112/// It currently works in units of nanoseconds.
113
114#[derive(Clone)]
115pub struct DurationTimer {
116    start:      Instant,
117    previous:   u128,
118}
119
120impl Timer for DurationTimer {
121    fn start(&mut self) {
122        self.start    = Instant::now();
123        self.previous = 0;
124    }
125
126    // Get the current elapsed time and subtract
127    // "previous" from it to get the time since the
128    // last "finish" call.  Then save this current
129    // time as the new "previous".
130
131    fn finish(&mut self) -> i64 {
132        let end_time  = self.start.elapsed().as_nanos();
133        let result    = end_time - self.previous;
134        self.previous = end_time;
135
136        std::cmp::min(result, i64::MAX as u128) as i64
137    }
138
139    // We read the clock in nanoseconds currently.
140
141    fn hz(&self) -> u128 {
142        1_000_000_000
143    }
144}
145
146impl DurationTimer {
147    pub fn new() -> DurationTimer {
148        let start    = Instant::now();
149        let previous = 0;
150
151        DurationTimer { start, previous }
152    }
153
154    pub fn new_box() -> TimerBox {
155        let timer = DurationTimer::new();
156
157        timer_box!(timer)
158    }
159}
160
161impl Default for DurationTimer {
162    fn default() -> Self {
163        Self::new()
164    }
165}
166
167/// SimpleClock can be implemented for platform-specific clocks.
168/// The instances can then be wrapped in a ClockTimer instance.
169
170pub trait SimpleClock {
171    /// Returns the current "time" in ticks, for whatever the
172    /// time means for the clock being used.  A cycle counter
173    /// will just return the current cycle count, for example.
174
175    fn get_time(&mut self) -> u128;
176
177    /// Returns the frequency of the clock.
178
179    fn hz(&self) -> u128;
180}
181
182/// The ClockTimer type is a wrapper class for platform-specific
183/// clocks.  It can be used for cycle counters and execution
184/// time clocks, for example.
185
186#[derive(Clone)]
187pub struct ClockTimer {
188    start:      u128,
189    clock:      Rc<RefCell<dyn SimpleClock>>,
190    hz:         u128,
191}
192
193impl Timer for ClockTimer {
194    fn start(&mut self) {
195        self.start = self.clock.borrow_mut().get_time();
196    }
197
198    fn finish(&mut self) -> i64 {
199        let end_time = self.clock.borrow_mut().get_time();
200        let ticks    = end_time - self.start;
201        self.start   = end_time;
202
203        ticks as i64
204    }
205
206    fn hz(&self) -> u128 {
207        self.hz
208    }
209}
210
211impl ClockTimer {
212    pub fn new(clock: Rc<RefCell<dyn SimpleClock>>) -> ClockTimer {
213        let start = clock.borrow_mut().get_time();
214        let hz    = clock.borrow().hz();
215
216        ClockTimer { start, clock, hz }
217    }
218
219    pub fn new_box(clock: Rc<RefCell<dyn SimpleClock>>) -> TimerBox {
220        let timer = ClockTimer::new(clock);
221
222        timer_box!(timer)
223    }
224}
225
226/// Converts a TimerBox instance into the shareable form,
227/// currently `Rc<RefCell<dyn Timer>>`.
228
229#[macro_export]
230macro_rules! timer_box { ($x:expr) => { Rc::from(RefCell::new($x)) } }
231
232/// Converts a TimerBox into a Timer reference.
233
234#[macro_export]
235macro_rules! timer { ($x:expr) => { &*$x.borrow() } }
236
237/// Converts a TimerBox into a mutable Timer reference.
238
239#[macro_export]
240macro_rules! timer_mut { ($x:expr) => { &mut *$x.borrow_mut() } }
241
242#[cfg(test)]
243pub mod tests {
244    use super::*;
245    use std::thread::sleep;
246    use std::time::Duration;
247
248    fn simple_duration_test() {
249        let mut clock         = DurationTimer::new();
250        let     seconds       = 1;
251        let     sleep_time    = Duration::new(seconds, 0);
252        let     base_interval = seconds as i64 * clock.hz() as i64;
253
254        clock.start();
255
256        for _i in 1..10 {
257            sleep(sleep_time);
258
259            let interval = clock.finish();
260
261            assert!(interval >= base_interval);
262            assert!(interval < base_interval + (base_interval / 20));
263        }
264    }
265
266    pub struct TestSimpleClock {
267        pub current:    u128,
268        pub increment:  u128,
269    }
270
271    impl SimpleClock for TestSimpleClock {
272        fn get_time(&mut self) -> u128 {
273            let result = self.current;
274
275            self.current   = self.current + self.increment;
276            result
277        }
278
279        fn hz(&self) -> u128 {
280            1_000_000_000
281        }
282    }
283
284    pub fn simple_test_clock() {
285        let current      = 0;
286        let increment    = 1500;
287        let simple_clock = timer_box!(TestSimpleClock { current, increment });
288        let clock        = ClockTimer::new_box(simple_clock);
289        let clock        = timer_mut!(clock);
290
291        // Creating the clock invokes get_time, so the increment in the
292        // test clock increases.  Keep ours in sync with it.
293
294        assert!(clock.hz() == 1_000_000_000);
295
296        clock.start();
297
298        for _i in 1..5 {
299            let interval = clock.finish();
300            assert!(interval == increment as i64);
301        }
302    }
303
304    // This is an example implementation of the SimpleClock
305    // trait.  It simple returns a series of time values
306    // incrementing by one in size per invocation.
307
308    struct ExampleClock {
309        current_time: u128,
310        hz:           u128,
311    }
312
313    impl ExampleClock {
314        fn new(start_time: u128, hz: u128) -> ExampleClock {
315            let current_time = start_time;
316
317            ExampleClock { current_time, hz }
318        }
319    }
320
321    impl SimpleClock for ExampleClock {
322        fn get_time(&mut self) -> u128 {
323            self.current_time += 1;
324            self.current_time
325        }
326
327        fn hz(&self) -> u128 {
328            self.hz
329        }
330    }
331
332    fn sample_usage() {
333        let     start_time  = 1;
334        let     hz          = 1_000_000_000;
335        let mut clock       = ExampleClock::new(start_time, hz);
336
337        // Get a few time values.
338
339        for i in 1..100 {
340            let time = clock.get_time();
341
342            assert!(time == start_time + i);
343        }
344
345        assert!(clock.hz() == hz);
346    }
347
348    fn simple_default_test() {
349        let mut timer = DurationTimer::default();
350
351        for _i in 1..100 {
352            let nanoseconds = timer.finish();
353            assert!(nanoseconds >= 0)
354        }
355    }
356
357    #[test]
358    pub fn run_tests() {
359        simple_duration_test();
360        simple_default_test ();
361        simple_test_clock   ();
362        sample_usage        ();
363    }
364}