summaryrefslogtreecommitdiff
path: root/src/accumulated_time.rs
blob: e633a105bb05c6155900a8620796cd862261bfa3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! Measure accumulated time for various operations.

use std::collections::HashMap;
use std::hash::Hash;
use std::sync::Mutex;
use std::time::Instant;

/// Accumulated times for different clocks.
///
/// The caller defines a clock type, usually an enum.
/// `AccumulatedTime` accumulates time for each possible clock.
/// Conceptually, every type of clock exists. If a type of clock
/// doesn't ever get created, it measures at 0 accumulated time.
#[derive(Debug)]
pub struct AccumulatedTime<T> {
    accumulated: Mutex<HashMap<T, ClockTime>>,
}

#[derive(Debug, Default)]
struct ClockTime {
    nanos: u128,
    started: Option<Instant>,
}

impl<T: Eq + PartialEq + Hash + Copy> AccumulatedTime<T> {
    /// Create a new accumulated time collector.
    #[allow(clippy::new_without_default)]
    pub fn new() -> Self {
        Self {
            accumulated: Mutex::new(HashMap::new()),
        }
    }

    /// Start a new clock of a given type to measure a span of time.
    ///
    /// The clock's measured time is added to the accumulator when the
    /// clock is stopped.
    pub fn start(&mut self, clock: T) {
        let mut map = self.accumulated.lock().unwrap();
        let ct = map.entry(clock).or_insert_with(ClockTime::default);
        assert!(ct.started.is_none());
        ct.started = Some(Instant::now());
    }

    /// Stop a running clock.
    ///
    /// Its run time is added to the accumulated time for that kind of clock.
    pub fn stop(&mut self, clock: T) {
        let mut map = self.accumulated.lock().unwrap();
        if let Some(mut ct) = map.get_mut(&clock) {
            assert!(ct.started.is_some());
            if let Some(started) = ct.started.take() {
                ct.nanos += started.elapsed().as_nanos();
                ct.started = None;
            }
        }
    }

    /// Return the accumulated time for a type of clock, as whole seconds.
    pub fn secs(&self, clock: T) -> u128 {
        self.nanos(clock) / 1_000_000_000u128
    }

    /// Return the accumulated time for a type of clock, as nanoseconds.
    ///
    /// This includes the time spent in a currently running clock.
    pub fn nanos(&self, clock: T) -> u128 {
        let map = self.accumulated.lock().unwrap();
        if let Some(ct) = map.get(&clock) {
            if let Some(started) = ct.started {
                ct.nanos + started.elapsed().as_nanos()
            } else {
                ct.nanos
            }
        } else {
            0
        }
    }
}