1use crate::os::sys;
9
10#[inline]
12pub fn now_ns() -> u64 {
13 #[cfg(unix)]
14 {
15 let mut ts = sys::timespec { tv_sec: 0, tv_nsec: 0 };
16 unsafe { sys::clock_gettime(sys::CLOCK_MONOTONIC, &mut ts) };
18 ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64
19 }
20 #[cfg(windows)]
21 {
22 use std::sync::atomic::{AtomicU64, Ordering};
23 static FREQ: AtomicU64 = AtomicU64::new(0);
24 let mut f = FREQ.load(Ordering::Relaxed);
25 if f == 0 {
26 let mut v = 0i64;
27 unsafe { sys::QueryPerformanceFrequency(&mut v) };
29 f = v.max(1) as u64;
30 FREQ.store(f, Ordering::Relaxed);
31 }
32 let mut c = 0i64;
33 unsafe { sys::QueryPerformanceCounter(&mut c) };
35 let c = c as u64;
36 (c / f) * 1_000_000_000 + (c % f) * 1_000_000_000 / f
38 }
39}
40
41pub fn wall_ns() -> u64 {
44 #[cfg(target_os = "linux")]
45 {
46 let mut ts = sys::timespec { tv_sec: 0, tv_nsec: 0 };
47 unsafe { sys::clock_gettime(sys::CLOCK_REALTIME, &mut ts) };
49 if ts.tv_sec < 0 {
50 return 0;
51 }
52 ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64
53 }
54 #[cfg(not(target_os = "linux"))]
55 {
56 std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map_or(0, |d| d.as_nanos() as u64)
57 }
58}
59
60#[inline]
62pub fn coarse_ns() -> u64 {
63 #[cfg(target_os = "linux")]
64 {
65 let mut ts = sys::timespec { tv_sec: 0, tv_nsec: 0 };
66 unsafe { sys::clock_gettime(sys::CLOCK_MONOTONIC_COARSE, &mut ts) };
68 ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64
69 }
70 #[cfg(not(target_os = "linux"))]
71 {
72 now_ns()
73 }
74}
75
76pub fn process_cpu_ns() -> u64 {
80 #[cfg(unix)]
81 {
82 let mut ts = sys::timespec { tv_sec: 0, tv_nsec: 0 };
83 unsafe { sys::clock_gettime(sys::CLOCK_PROCESS_CPUTIME_ID, &mut ts) };
85 ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64
86 }
87 #[cfg(windows)]
88 {
89 let (mut c, mut e, mut k, mut u) = (0u64, 0u64, 0u64, 0u64);
90 unsafe { sys::GetProcessTimes(sys::GetCurrentProcess(), &mut c, &mut e, &mut k, &mut u) };
92 (k + u) * 100
93 }
94}
95
96#[cfg(test)]
98pub(crate) fn thread_cpu_ns_for_tests() -> u64 {
99 #[cfg(unix)]
100 {
101 let mut ts = sys::timespec { tv_sec: 0, tv_nsec: 0 };
102 unsafe { sys::clock_gettime(sys::CLOCK_THREAD_CPUTIME_ID, &mut ts) };
104 ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64
105 }
106 #[cfg(windows)]
107 {
108 let (mut c, mut e, mut k, mut u) = (0u64, 0u64, 0u64, 0u64);
109 unsafe { sys::GetThreadTimes(sys::GetCurrentThread(), &mut c, &mut e, &mut k, &mut u) };
111 (k + u) * 100
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn cpu_clocks_count_work_not_sleep() {
121 let (p0, t0) = (process_cpu_ns(), thread_cpu_ns_for_tests());
122 std::thread::sleep(std::time::Duration::from_millis(50));
123 let t1 = thread_cpu_ns_for_tests();
124 assert!(t1 - t0 < 30_000_000, "sleeping used {} ns of thread CPU", t1 - t0);
125 let end = now_ns() + 60_000_000;
126 let mut x = 0u64;
127 while now_ns() < end {
128 x = x.wrapping_add(1);
129 }
130 let (p2, t2) = (process_cpu_ns(), thread_cpu_ns_for_tests());
131 assert!(t2 - t1 >= 20_000_000, "spinning 60 ms counted {} ns ({x})", t2 - t1);
132 assert!(p2 - p0 >= t2 - t1 - 16_000_000);
133 }
134
135 #[test]
136 fn clocks_are_monotonic_and_track_real_time() {
137 let a = now_ns();
138 let c0 = coarse_ns();
139 std::thread::sleep(std::time::Duration::from_millis(30));
140 let b = now_ns();
141 let c1 = coarse_ns();
142 assert!(b >= a + 25_000_000, "{a} {b}");
143 assert!(b < a + 5_000_000_000);
144 assert!(c1 >= c0 + 10_000_000);
145 let n = 1_000_000;
147 let t = std::time::Instant::now();
148 let mut x = 0u64;
149 for _ in 0..n {
150 x = x.wrapping_add(now_ns());
151 }
152 let precise = t.elapsed().as_nanos() as f64 / n as f64;
153 let t = std::time::Instant::now();
154 for _ in 0..n {
155 x = x.wrapping_add(coarse_ns());
156 }
157 let coarse = t.elapsed().as_nanos() as f64 / n as f64;
158 eprintln!("now_ns {precise:.1} ns, coarse_ns {coarse:.1} ns per read ({x})");
159 }
160}