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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
mod coarse_now;
mod instant;
#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
mod tsc_now;
pub use instant::{Anchor, Instant};
#[inline]
pub fn is_tsc_available() -> bool {
#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
{
tsc_now::is_tsc_available()
}
#[cfg(not(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64"))))]
{
false
}
}
#[inline]
pub(crate) fn current_cycle() -> u64 {
#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
if is_tsc_available() {
tsc_now::current_cycle()
} else {
coarse_now::current_cycle()
}
#[cfg(not(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64"))))]
{
coarse_now::current_cycle()
}
}
#[inline]
pub(crate) fn nanos_per_cycle() -> f64 {
#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
{
tsc_now::nanos_per_cycle()
}
#[cfg(not(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64"))))]
{
1.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::Rng;
use std::time::{Duration, Instant as StdInstant};
#[test]
fn test_is_tsc_available() {
let _ = is_tsc_available();
}
#[test]
fn test_monotonic() {
let mut prev = 0;
for _ in 0..10000 {
let cur = current_cycle();
assert!(cur >= prev);
prev = cur;
}
}
#[test]
fn test_nanos_per_cycle() {
let _ = nanos_per_cycle();
}
#[test]
fn test_duration() {
let mut rng = rand::thread_rng();
for _ in 0..10 {
let instant = Instant::now();
let std_instant = StdInstant::now();
std::thread::sleep(Duration::from_millis(rng.gen_range(100..500)));
let check = move || {
let duration_ns_minstant = instant.elapsed();
let duration_ns_std = std_instant.elapsed();
#[cfg(target_os = "windows")]
let expect_max_delta_ns = 20_000_000;
#[cfg(not(target_os = "windows"))]
let expect_max_delta_ns = 5_000_000;
let real_delta = (duration_ns_std.as_nanos() as i128
- duration_ns_minstant.as_nanos() as i128)
.abs();
assert!(
real_delta < expect_max_delta_ns,
"real delta: {}",
real_delta
);
};
check();
std::thread::spawn(check).join().expect("join failed");
}
}
}