teaql_tool_std/
high_res_timer.rs1#[derive(Debug, Clone)]
2pub struct HighResTimerTool;
3
4impl HighResTimerTool {
5 pub fn new() -> Self {
6 Self
7 }
8
9 pub fn start(&self) -> HighResTimer {
11 HighResTimer::start()
12 }
13}
14
15impl Default for HighResTimerTool {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21#[cfg(not(windows))]
22mod imp {
23 use std::sync::OnceLock;
24
25 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
26 use core::arch::x86_64::{_mm_lfence, _rdtsc};
27
28 #[derive(Debug, Clone)]
29 pub struct HighResTimer {
30 start_cycles: u64,
31 }
32
33 static TICK_HZ: OnceLock<u64> = OnceLock::new();
34
35 #[inline(always)]
36 fn global_tick_hz() -> u64 {
37 *TICK_HZ.get_or_init(|| calibrate_tick_hz())
38 }
39
40 impl HighResTimer {
41 pub fn start() -> Self {
42 let _ = global_tick_hz();
43 Self { start_cycles: Self::get_ticks() }
44 }
45
46 #[inline(always)]
47 fn get_ticks() -> u64 {
48 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
49 unsafe {
50 _mm_lfence();
51 let t = _rdtsc();
52 _mm_lfence();
53 return t;
54 }
55
56 #[cfg(target_arch = "aarch64")]
57 {
58 let val: u64;
59 unsafe {
60 core::arch::asm!("mrs {}, cntvct_el0", out(reg) val);
61 }
62 return val;
63 }
64
65 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
66 {
67 0
68 }
69 }
70
71 pub fn ns(&self) -> u64 {
72 let end_ticks = Self::get_ticks();
73 let delta = end_ticks.wrapping_sub(self.start_cycles) as u128;
74 ((delta * 1_000_000_000u128) / global_tick_hz() as u128) as u64
75 }
76 }
77
78 fn calibrate_tick_hz() -> u64 {
79 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
80 {
81 return calibrate_tsc_with_monotonic();
82 }
83
84 #[cfg(target_arch = "aarch64")]
85 {
86 return read_cntfrq_el0();
87 }
88
89 #[allow(unreachable_code)]
90 2_500_000_000
91 }
92
93 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
94 fn calibrate_tsc_with_monotonic() -> u64 {
95 use libc::{clock_gettime, timespec, CLOCK_MONOTONIC};
96
97 unsafe {
98 let mut ts_start = timespec { tv_sec: 0, tv_nsec: 0 };
99 let mut ts_end = timespec { tv_sec: 0, tv_nsec: 0 };
100
101 clock_gettime(CLOCK_MONOTONIC, &mut ts_start);
102 _mm_lfence();
103 let tsc_start = _rdtsc();
104 _mm_lfence();
105
106 spin_wait_ns(10_000_000); clock_gettime(CLOCK_MONOTONIC, &mut ts_end);
109 _mm_lfence();
110 let tsc_end = _rdtsc();
111 _mm_lfence();
112
113 let delta_tsc = tsc_end - tsc_start;
114 let delta_ns = (ts_end.tv_sec - ts_start.tv_sec) as u128 * 1_000_000_000u128
115 + (ts_end.tv_nsec - ts_start.tv_nsec) as u128;
116
117 (delta_tsc as u128 * 1_000_000_000u128 / delta_ns) as u64
118 }
119 }
120
121 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
122 #[inline(always)]
123 fn spin_wait_ns(ns: u64) {
124 use libc::{clock_gettime, timespec, CLOCK_MONOTONIC};
125
126 let start = unsafe {
127 let mut ts = timespec { tv_sec: 0, tv_nsec: 0 };
128 clock_gettime(CLOCK_MONOTONIC, &mut ts);
129 ts.tv_sec as u128 * 1_000_000_000u128 + ts.tv_nsec as u128
130 };
131
132 loop {
133 let now = unsafe {
134 let mut ts = timespec { tv_sec: 0, tv_nsec: 0 };
135 clock_gettime(CLOCK_MONOTONIC, &mut ts);
136 ts.tv_sec as u128 * 1_000_000_000u128 + ts.tv_nsec as u128
137 };
138 if now - start >= ns as u128 {
139 break;
140 }
141 }
142 }
143
144 #[cfg(target_arch = "aarch64")]
145 #[inline(always)]
146 fn read_cntfrq_el0() -> u64 {
147 let freq: u64;
148 unsafe {
149 core::arch::asm!("mrs {}, cntfrq_el0", out(reg) freq);
150 }
151 freq
152 }
153}
154
155#[cfg(windows)]
156mod imp {
157 use std::time::Instant;
158
159 #[derive(Debug, Clone)]
160 pub struct HighResTimer {
161 start_time: Instant,
162 }
163
164 impl HighResTimer {
165 pub fn start() -> Self {
166 Self { start_time: Instant::now() }
167 }
168
169 pub fn ns(&self) -> u64 {
170 self.start_time.elapsed().as_nanos() as u64
171 }
172 }
173}
174
175pub use imp::HighResTimer;
176
177impl HighResTimer {
178 #[inline(always)]
179 pub fn us(&self) -> u64 {
180 self.ns() / 1_000
181 }
182
183 #[inline(always)]
184 pub fn ms(&self) -> u64 {
185 self.ns() / 1_000_000
186 }
187}