sol_trade_sdk/common/
fast_timing.rs1use crate::perf::syscall_bypass::SystemCallBypassManager;
6use once_cell::sync::Lazy;
7use std::time::{Duration, Instant};
8
9static FAST_TIMER: Lazy<FastTimer> = Lazy::new(|| FastTimer::new());
11
12pub struct FastTimer {
14 bypass_manager: SystemCallBypassManager,
15 _base_instant: Instant,
16 _base_nanos: u64,
17}
18
19impl FastTimer {
20 fn new() -> Self {
21 use crate::perf::syscall_bypass::SyscallBypassConfig;
22
23 let bypass_manager = SystemCallBypassManager::new(SyscallBypassConfig::default())
24 .expect("Failed to create SystemCallBypassManager");
25
26 let base_instant = Instant::now();
27 let base_nanos = bypass_manager.fast_timestamp_nanos();
28
29 Self { bypass_manager, _base_instant: base_instant, _base_nanos: base_nanos }
30 }
31
32 #[inline(always)]
34 pub fn now_nanos(&self) -> u64 {
35 self.bypass_manager.fast_timestamp_nanos()
36 }
37
38 #[inline(always)]
40 pub fn now_micros(&self) -> u64 {
41 self.now_nanos() / 1_000
42 }
43
44 #[inline(always)]
46 pub fn now_millis(&self) -> u64 {
47 self.now_nanos() / 1_000_000
48 }
49
50 #[inline(always)]
52 pub fn elapsed_nanos(&self, start_nanos: u64) -> u64 {
53 self.now_nanos().saturating_sub(start_nanos)
54 }
55
56 #[inline(always)]
58 pub fn elapsed_duration(&self, start_nanos: u64) -> Duration {
59 Duration::from_nanos(self.elapsed_nanos(start_nanos))
60 }
61}
62
63#[inline(always)]
67pub fn fast_now_nanos() -> u64 {
68 FAST_TIMER.now_nanos()
69}
70
71#[inline(always)]
73pub fn fast_now_micros() -> u64 {
74 FAST_TIMER.now_micros()
75}
76
77#[inline(always)]
79pub fn fast_now_millis() -> u64 {
80 FAST_TIMER.now_millis()
81}
82
83#[inline(always)]
85pub fn fast_elapsed_nanos(start_nanos: u64) -> u64 {
86 FAST_TIMER.elapsed_nanos(start_nanos)
87}
88
89#[inline(always)]
91pub fn fast_elapsed(start_nanos: u64) -> Duration {
92 FAST_TIMER.elapsed_duration(start_nanos)
93}
94
95pub struct FastStopwatch {
97 start_nanos: u64,
98 #[allow(dead_code)]
99 label: &'static str,
100}
101
102impl FastStopwatch {
103 #[inline(always)]
105 pub fn start(label: &'static str) -> Self {
106 Self { start_nanos: fast_now_nanos(), label }
107 }
108
109 #[inline(always)]
111 pub fn elapsed_nanos(&self) -> u64 {
112 fast_elapsed_nanos(self.start_nanos)
113 }
114
115 #[inline(always)]
117 pub fn elapsed(&self) -> Duration {
118 fast_elapsed(self.start_nanos)
119 }
120
121 #[inline(always)]
123 pub fn elapsed_micros(&self) -> u64 {
124 self.elapsed_nanos() / 1_000
125 }
126
127 #[inline(always)]
129 pub fn elapsed_millis(&self) -> u64 {
130 self.elapsed_nanos() / 1_000_000
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_fast_timing() {
140 let start = fast_now_nanos();
141 std::thread::sleep(Duration::from_millis(10));
142 let elapsed = fast_elapsed_nanos(start);
143
144 assert!(elapsed >= 9_000_000);
146 }
147
148 #[test]
149 fn test_stopwatch() {
150 let sw = FastStopwatch::start("test");
151 std::thread::sleep(Duration::from_millis(10));
152 let elapsed_ms = sw.elapsed_millis();
153
154 assert!(elapsed_ms >= 9);
155 }
156
157 #[test]
158 fn test_fast_now_overhead() {
159 let iterations = 100_000;
163 for _ in 0..1_000 {
164 let _ = fast_now_nanos();
165 }
166 let start = Instant::now();
167
168 for _ in 0..iterations {
169 let _ = fast_now_nanos();
170 }
171
172 let total_elapsed = start.elapsed();
173 let avg_per_call = total_elapsed.as_nanos() / iterations;
174
175 if crate::common::sdk_log::sdk_log_enabled() {
176 println!("Average fast_now_nanos() call: {}ns", avg_per_call);
177 }
178
179 assert!(avg_per_call < 500);
181 }
182
183 #[test]
184 fn test_instant_now_overhead() {
185 let iterations = 10_000;
187 let start = Instant::now();
188
189 for _ in 0..iterations {
190 let _ = Instant::now();
191 }
192
193 let total_elapsed = start.elapsed();
194 let avg_per_call = total_elapsed.as_nanos() / iterations;
195
196 if crate::common::sdk_log::sdk_log_enabled() {
197 println!("Average Instant::now() call: {}ns", avg_per_call);
198 }
199 }
200}