Skip to main content

diskann_disk/utils/instrumentation/
timer.rs

1/*
2 * Copyright (c) Microsoft Corporation.
3 * Licensed under the MIT license.
4 */
5use std::time::{Duration, Instant};
6
7#[cfg(target_os = "linux")]
8mod linux;
9#[cfg(target_os = "linux")]
10use linux::{
11    get_number_of_processors, get_peak_workingset_size, get_process_cycle_time, get_process_time,
12    get_system_time,
13};
14#[cfg(target_os = "macos")]
15mod macos;
16#[cfg(target_os = "macos")]
17use macos::{
18    get_number_of_processors, get_peak_workingset_size, get_process_cycle_time, get_process_time,
19    get_system_time,
20};
21#[cfg(target_os = "windows")]
22mod windows;
23#[cfg(target_os = "windows")]
24use windows::{
25    get_number_of_processors, get_peak_workingset_size, get_process_cycle_time, get_process_time,
26    get_system_time,
27};
28
29#[derive(Clone)]
30pub struct Timer {
31    check_point: Instant,
32    cycles: Option<u64>,
33    start_process_time: Option<u64>,
34    start_system_time: Option<u64>,
35    number_of_processors: Option<u64>,
36}
37
38impl Default for Timer {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44impl Timer {
45    pub fn new() -> Timer {
46        let cycles = get_process_cycle_time();
47        Timer {
48            check_point: Instant::now(),
49            cycles,
50            start_process_time: get_process_time(),
51            start_system_time: get_system_time(),
52            number_of_processors: get_number_of_processors(),
53        }
54    }
55
56    pub fn reset(&mut self) {
57        self.check_point = Instant::now();
58        self.cycles = get_process_cycle_time();
59        self.start_process_time = get_process_time();
60        self.start_system_time = get_system_time();
61    }
62
63    pub fn elapsed(&self) -> Duration {
64        Instant::now().duration_since(self.check_point)
65    }
66
67    pub fn elapsed_gcycles(&self) -> f32 {
68        let cur_cycles = get_process_cycle_time();
69        if let (Some(cur_cycles), Some(cycles)) = (cur_cycles, self.cycles) {
70            let spent_cycles =
71                ((cur_cycles - cycles) as f64 * 1.0f64) / (1024 * 1024 * 1024) as f64;
72            return spent_cycles as f32;
73        }
74
75        0.0
76    }
77
78    // Returns the average CPU time in percents (100% means that only one core is used)
79    pub fn get_average_cpu_time_in_percents(&self) -> f64 {
80        let cur_process_time = get_process_time();
81        let cur_system_time = get_system_time();
82        if let (
83            Some(cur_process_time),
84            Some(cur_system_time),
85            Some(start_process_time),
86            Some(start_system_time),
87            Some(number_of_processors),
88        ) = (
89            cur_process_time,
90            cur_system_time,
91            self.start_process_time,
92            self.start_system_time,
93            self.number_of_processors,
94        ) {
95            let process_time_delta = cur_process_time - start_process_time;
96            let system_time_delta = cur_system_time - start_system_time;
97
98            if system_time_delta > 0 {
99                return (process_time_delta as f64) / (system_time_delta as f64)
100                    * number_of_processors as f64
101                    * 100f64;
102            }
103        }
104
105        0.0
106    }
107
108    pub fn get_peak_memory_usage(&self) -> f32 {
109        let memory_in_bytes = get_peak_workingset_size();
110        if let Some(bytes) = memory_in_bytes {
111            let memory_in_gbs = bytes as f64 / (1024 * 1024 * 1024) as f64;
112            return memory_in_gbs as f32;
113        }
114
115        0.0
116    }
117}
118
119#[cfg(test)]
120mod timer_tests {
121    use std::{thread, time};
122
123    use super::*;
124
125    #[test]
126    #[allow(clippy::if_same_then_else)]
127    fn test_new() {
128        let timer = Timer::new();
129        if cfg!(windows) {
130            assert!(timer.cycles.is_some());
131        } else if cfg!(target_os = "linux") {
132            assert!(timer.cycles.is_some());
133        } else if cfg!(target_os = "macos") {
134            assert!(timer.cycles.is_some());
135        } else {
136            panic!("No timer::test_new defined for current configuration");
137        }
138    }
139
140    #[test]
141    fn test_reset() {
142        let mut timer = Timer::new();
143        let checkpoint_before_reset = timer.check_point;
144
145        for _ in 0..10 {
146            timer.reset();
147            let checkpoint_after_reset = timer.check_point;
148            if checkpoint_after_reset > checkpoint_before_reset {
149                return;
150            }
151
152            thread::sleep(time::Duration::from_millis(10));
153        }
154
155        timer.reset();
156
157        assert!(
158            timer.check_point > checkpoint_before_reset,
159            "Timer::reset() did not update check_point"
160        );
161    }
162
163    #[test]
164    fn test_elapsed() {
165        let timer = Timer::new();
166        let t0 = timer.elapsed();
167
168        for _ in 0..10 {
169            let t1 = timer.elapsed();
170            if t1 > t0 {
171                return;
172            }
173
174            thread::sleep(time::Duration::from_millis(10));
175        }
176
177        assert!(
178            timer.elapsed() > t0,
179            "Timer::elapsed() did not increase over time"
180        );
181    }
182
183    #[test]
184    fn test_get_average_cpu_time_in_percents() {
185        let timer = Timer::new();
186        thread::sleep(time::Duration::from_millis(10));
187        assert!(timer.get_average_cpu_time_in_percents() >= 0f64);
188    }
189
190    #[test]
191    fn test_get_peak_memory_usage() {
192        let timer = Timer::new();
193        let peak_memory_usage = timer.get_peak_memory_usage();
194        assert!(peak_memory_usage >= 0.0);
195    }
196}