time_elapsed/
lib.rs

1//! A Rust crate that provides a concise and handy way to benchmark **elapsed time inside functions**.
2//! > time-elapsed brings a small overhead, however, if you are trying to measure very small durations (in the order of *nanoseconds* or few *microseconds*), please consider using something else.
3//!
4//! ### installation
5//! Add the following to Cargo.toml
6//! ```
7//! [dependencies]
8//! time-elapsed = "0.1.0"
9//! ```
10//! 
11//! # features
12//! * named benchmark
13//! * timestamps
14//! * coloured messages
15//! * auto unit of measurement
16//! 
17//! # example
18//! 
19//! ### code
20//! 
21//! ```
22//! use std::thread;
23//! use std::time::Duration;
24//! 
25//! use time_elapsed;
26//! 
27//! fn main() {
28//!     let mut time = time_elapsed::start("test");
29//! 
30//!     // sleep 200 ms
31//!     thread::sleep(Duration::from_millis(200));
32//! 
33//!     time
34//!         .log("log() prints a message and the time elapsed")
35//!         .timestamp();
36//! 
37//!     // sleep 2 ms
38//!     thread::sleep(Duration::from_millis(2));
39//! 
40//!     time.log("this is an offset from the previous timestamp()");
41//! 
42//!     time.log_overall("log_overall() ignores timestamps");
43//! 
44//!     time.end();
45//! }
46//! ```
47//! 
48//! ### output
49//! 
50//! ```console
51//! running test...
52//! (test) log() prints a message and the time elapsed -> 200ms
53//! (test) this is an offset from the previous timestamp() -> 2103 μs
54//! (test) log_overall() ignores timestamps -> 202 ms
55//! test finished in 202 ms (202271 μs)
56//! ```
57//!
58
59use std::time::Instant;
60
61/// Starts the benchmark by returning an initialized instance of **TimeElpased**.
62/// 
63/// # example
64/// 
65/// ```
66/// let mut time = time_elapsed::start("test");
67/// // output: running test...
68/// ```
69pub fn start<S: AsRef<str>>(name: S) -> TimeElapsed {
70    TimeElapsed::new(name.as_ref())
71}
72
73fn get_unit_of_measurement(nanos: u128) -> &'static str {
74    match nanos / 4000000 {
75        0 => "μs",
76        _ => match nanos / 15000000000 {
77            0 => "ms",
78            _ => match nanos / 300000000000 {
79                0 => "s",
80                _ => match nanos / 540000000000 {
81                    0 => "min",
82                    _ => "hrs",
83                },
84            },
85        },
86    }
87}
88
89fn get_units_of_measurement(nanos: u128) -> [&'static str; 2] {
90    match get_unit_of_measurement(nanos) {
91        "μs" => ["μs", "ns"],
92        "ms" => ["ms", "μs"],
93        "s" => ["s", "ms"],
94        "min" => ["min", "s"],
95        "hrs" => ["hrs", "min"],
96        _ => ["ns", "ns"],
97    }
98}
99
100fn nanos_to_unit_of_msr(nanos: u128, unit_of_msr: &str) -> u128 {
101    match unit_of_msr {
102        "μs" => nanos / 1000,
103        "ms" => nanos / 1000000,
104        "s" => nanos / 1000000000,
105        "min" => nanos / 60000000000,
106        "hrs" => nanos / 3600000000000,
107        _ => nanos,
108    }
109}
110
111fn nanos_to_units_of_msr(nanos: u128, unit_of_msr: &str) -> [u128; 2] {
112    match unit_of_msr {
113        "μs" => [nanos / 1000, nanos],
114        "ms" => [nanos / 1000000, nanos / 1000],
115        "s" => [nanos / 1000000000, nanos / 1000000],
116        "min" => [nanos / 60000000000, nanos / 1000000000],
117        "hrs" => [nanos / 3600000000000, nanos / 60000000000],
118        _ => [nanos, nanos],
119    }
120}
121
122/// Stores the benchmark state and provides methods (timestamp method needs a mutable reference).
123/// 
124/// To create an initialized instance use the **time_elapsed::start** function.
125/// 
126/// # example
127/// 
128/// ```
129/// let mut time = time_elapsed::start("test");
130/// // output: running test...
131/// 
132/// ```
133#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
134pub struct TimeElapsed {
135    name: String,
136    start_timestamp: Instant,
137    last_timestamp: Instant,
138}
139
140impl TimeElapsed {
141
142    fn new(name: &str) -> Self {
143        println!("running {}...", name);
144        Self {
145            name: name.to_string(),
146            start_timestamp: Instant::now(),
147            last_timestamp: Instant::now(),
148        }
149    }
150
151    fn print_message(&mut self, msg: &str, nanos: u128) -> &Self {
152        let unit = get_unit_of_measurement(nanos);
153        let time = nanos_to_unit_of_msr(nanos, unit);
154        println!(
155            "(\x1b[32m\x1b[1m{}\x1b[0m) \x1b[1m{} \x1b[0m-> \x1b[35m\x1b[1m{} {} \x1b[0m",
156            self.name, msg, time, unit
157        );
158        self
159    }
160
161    /// Ends the benchmark. Outputs the total elapsed time from the start
162    /// of the benchmark.
163    /// 
164    /// # example
165    /// 
166    /// ```
167    /// let mut time = time_elapsed::start("test");
168    /// // output: running test...
169    /// 
170    /// time.end();
171    /// // output: test finished in 1 μs (1204 ns)
172    /// 
173    /// ```
174    pub fn end(self) {
175        let nanos = self.start_timestamp.elapsed().as_nanos();
176        let units = get_units_of_measurement(nanos);
177        let times = nanos_to_units_of_msr(nanos, units[0]);
178        println!(
179            "\x1b[32m\x1b[1m{} finished\x1b[0m in \x1b[35m\x1b[1m{} {} \x1b[0m({} {})",
180            self.name, times[0], units[0], times[1], units[1],
181        );
182    }
183
184    /// Outputs a message followed by the **elapsed time** from the **previous timestamp**.
185    /// 
186    /// Returns a mutable reference of self.
187    /// 
188    /// # example
189    /// 
190    /// ```
191    /// let mut time = time_elapsed::start("test");
192    /// // output: running test...
193    /// 
194    /// time.log("My message");
195    /// // output: (test) My message -> 1 μs
196    /// 
197    /// ```
198    pub fn log<S: AsRef<str>>(&mut self, msg: S) -> &mut Self {
199        let nanos = self.last_timestamp.elapsed().as_nanos();
200        self.print_message(msg.as_ref(), nanos);
201        self
202    }
203
204    /// Outputs a message followed by the **elapsed time** from the **start**, ignoring timestamps.
205    /// 
206    /// Returns a mutable reference of self.
207    /// 
208    /// # example
209    /// 
210    /// ```
211    /// use std::thread;
212    /// use std::time::Duration;
213    /// 
214    /// let mut time = time_elapsed::start("test");
215    /// // output: running test...
216    /// 
217    /// thread::sleep(Duration::from_millis(200));
218    /// 
219    /// time.timestamp();
220    /// time.log_overall("The elapsed time from the start");
221    /// // output: (test) The elapsed time from the start -> 200 ms
222    /// 
223    /// ```
224    pub fn log_overall<S: AsRef<str>>(&mut self, msg: S) -> &mut Self {
225        let nanos = self.start_timestamp.elapsed().as_nanos();
226        self.print_message(msg.as_ref(), nanos);
227        self
228    }
229
230    /// Updates and returns the last timestamp.
231    /// 
232    /// # example
233    /// 
234    /// ```
235    /// use std::thread;
236    /// use std::time::Duration;
237    /// let mut time = time_elapsed::start("test");
238    /// // output: running test...
239    /// 
240    /// thread::sleep(Duration::from_millis(200));
241    /// 
242    /// time.timestamp();
243    /// 
244    /// time.log("Elapsed time from the prev timestamp");
245    /// // output: (test) Elapsed time from the prev timestamp -> 1 μs
246    /// 
247    /// ```
248    pub fn timestamp(&mut self) -> Instant {
249        self.last_timestamp = Instant::now();
250        self.last_timestamp
251    }
252}