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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use std::{fmt, time::Instant};

use necsim_core::{impl_finalise, impl_report, reporter::Reporter};

#[allow(clippy::module_name_repetitions)]
pub struct ExecutionTimeReporter {
    init_time: Instant,
    start_time: Option<Instant>,
    end_time: Option<Instant>,
}

impl fmt::Debug for ExecutionTimeReporter {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct(stringify!(ExecutionTimeReporter))
            .field(
                "start_time",
                &self
                    .start_time
                    .as_ref()
                    .map(|time| time.duration_since(self.init_time)),
            )
            .field(
                "end_time",
                &self
                    .end_time
                    .as_ref()
                    .map(|time| time.duration_since(self.init_time)),
            )
            .finish_non_exhaustive()
    }
}

impl serde::Serialize for ExecutionTimeReporter {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_unit()
    }
}

impl<'de> serde::Deserialize<'de> for ExecutionTimeReporter {
    fn deserialize<D: serde::Deserializer<'de>>(_deserializer: D) -> Result<Self, D::Error> {
        Ok(Self::default())
    }
}

impl Reporter for ExecutionTimeReporter {
    impl_report!(speciation(&mut self, _speciation: Ignored) {});

    impl_report!(dispersal(&mut self, _dispersal: Ignored) {});

    impl_report!(
        #[debug_ensures(self.start_time.is_some(), "start_time is set after first call")]
        progress(&mut self, remaining: Used) {
            if self.start_time.is_none() {
                self.start_time = Some(Instant::now());
            }

            if *remaining == 0 {
                self.end_time = Some(Instant::now());
            }
        }
    );

    impl_finalise!((mut self) {
        match (self.start_time, self.end_time) {
            (Some(start_time), Some(end_time)) => info!(
                "The simulation took:\n - initialisation: {:?}\n - execution: {:?}\n - \
                cleanup: {:?}",
                (start_time - self.init_time),
                (end_time - start_time),
                end_time.elapsed()
            ),
            (Some(start_time), None) => info!(
                "The simulation took:\n - initialisation: {:?}\n - execution: {:?}",
                (start_time - self.init_time),
                start_time.elapsed(),
            ),
            _ => (),
        };
    });

    fn initialise(&mut self) -> Result<(), String> {
        self.init_time = Instant::now();
        self.start_time = None;
        self.end_time = None;

        Ok(())
    }
}

impl Default for ExecutionTimeReporter {
    #[debug_ensures(ret.start_time.is_none(), "initialises start_time to None")]
    #[debug_ensures(ret.end_time.is_none(), "initialises end_time to None")]
    fn default() -> Self {
        Self {
            init_time: Instant::now(),
            start_time: None,
            end_time: None,
        }
    }
}