cfpyo3_core/toolkit/
misc.rs1#[cfg(feature = "tokio")]
2use anyhow::Result;
3use md5::{Digest, Md5};
4use std::{collections::HashMap, fmt, sync::RwLock, time::Instant};
5#[cfg(feature = "tokio")]
6use tokio::runtime::{Builder, Runtime};
7
8pub fn hash_code(code: &str) -> String {
9 let mut hasher = Md5::new();
10 hasher.update(code.as_bytes());
11 format!("{:x}", hasher.finalize())
12}
13
14#[derive(Clone)]
16pub struct Tracker {
17 history: Vec<f64>,
18 tracking: Option<Instant>,
19}
20pub struct Stats {
21 n: usize,
22 mean: f64,
23 std: f64,
24 is_fast_path: bool,
25 is_bottleneck: bool,
26}
27impl Tracker {
28 pub const fn new() -> Self {
29 Self {
30 history: Vec::new(),
31 tracking: None,
32 }
33 }
34
35 pub fn track(&mut self, time: f64) {
36 self.history.push(time);
37 }
38
39 pub fn track_start(&mut self) {
40 self.tracking = Some(Instant::now());
41 }
42
43 pub fn track_end(&mut self) {
44 let time = self
45 .tracking
46 .expect("please call `track_start` before `track_end`")
47 .elapsed()
48 .as_secs_f64();
49 self.history.push(time);
50 self.tracking = None;
51 }
52
53 pub fn reset(&mut self) {
54 self.history.clear();
55 self.tracking = None;
56 }
57
58 pub fn get_stats(&self) -> Stats {
59 let n = self.history.len();
60 let mean = self.history.iter().sum::<f64>() / n as f64;
61 let variance = self.history.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / n as f64;
62 let std = variance.sqrt();
63 Stats {
64 n,
65 mean,
66 std,
67 is_fast_path: false,
68 is_bottleneck: false,
69 }
70 }
71}
72impl Default for Tracker {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77impl fmt::Debug for Stats {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 let sum = self.n as f64 * self.mean;
80 let prefix = if self.is_bottleneck {
81 format!("[🚨 {:.8}] ", sum)
82 } else if self.is_fast_path {
83 format!("[⚡️ {:.8}] ", sum)
84 } else {
85 format!("[ {:.8}] ", sum)
86 };
87 write!(
88 f,
89 "{}{:.8} ± {:.8} ({})",
90 prefix, self.mean, self.std, self.n
91 )
92 }
93}
94
95pub struct Trackers(pub Vec<RwLock<Tracker>>);
97impl Trackers {
98 pub fn new(n: usize) -> Self {
99 Self((0..n).map(|_| RwLock::new(Tracker::new())).collect())
100 }
101
102 pub fn track(&self, idx: usize, time: f64) {
103 self.0[idx].write().unwrap().track(time);
104 }
105
106 pub fn track_start(&self, idx: usize) {
107 self.0[idx].write().unwrap().track_start();
108 }
109
110 pub fn track_end(&self, idx: usize) {
111 self.0[idx].write().unwrap().track_end();
112 }
113
114 pub fn reset(&self) {
115 self.0
116 .iter()
117 .for_each(|tracker| tracker.write().unwrap().reset());
118 }
119
120 pub fn get_stats(&self) -> Vec<Stats> {
121 let mut stats: Vec<Stats> = self
122 .0
123 .iter()
124 .map(|tracker| tracker.read().unwrap().get_stats())
125 .collect();
126 let mut fast_path_idx = 0;
127 let mut bottleneck_idx = 0;
128 let mut fast_path_t = stats[0].n as f64 * stats[0].mean;
129 let mut bottleneck_t = fast_path_t;
130 for (idx, s) in stats.iter().enumerate() {
131 let new_t = s.n as f64 * s.mean;
132 if new_t > bottleneck_t {
133 bottleneck_idx = idx;
134 bottleneck_t = new_t;
135 } else if new_t < fast_path_t {
136 fast_path_idx = idx;
137 fast_path_t = new_t;
138 }
139 }
140 stats[fast_path_idx].is_fast_path = true;
141 stats[bottleneck_idx].is_bottleneck = true;
142 stats
143 }
144}
145
146pub struct NamedTrackers(pub HashMap<String, RwLock<Tracker>>);
148impl NamedTrackers {
149 pub fn new(names: Vec<String>) -> Self {
150 Self(
151 names
152 .into_iter()
153 .map(|name| (name, RwLock::new(Tracker::new())))
154 .collect(),
155 )
156 }
157
158 fn get(&self, name: &str) -> &RwLock<Tracker> {
159 self.0
160 .get(name)
161 .unwrap_or_else(|| panic!("'{}' not found in current trackers", name))
162 }
163
164 pub fn track(&self, name: &str, time: f64) {
165 self.get(name).write().unwrap().track(time);
166 }
167
168 pub fn track_start(&self, name: &str) {
169 self.get(name).write().unwrap().track_start();
170 }
171
172 pub fn track_end(&self, name: &str) {
173 self.get(name).write().unwrap().track_end();
174 }
175
176 pub fn reset(&self) {
177 self.0
178 .iter()
179 .for_each(|(_, tracker)| tracker.write().unwrap().reset());
180 }
181
182 pub fn get_stats(&self) -> HashMap<String, Stats> {
183 self.0
184 .iter()
185 .map(|(name, tracker)| (name.clone(), tracker.read().unwrap().get_stats()))
186 .collect()
187 }
188}
189
190#[cfg(feature = "tokio")]
193pub fn init_rt(num_threads: usize) -> Result<Runtime> {
194 let rt = if num_threads <= 1 {
195 Builder::new_current_thread().enable_all().build()?
196 } else {
197 Builder::new_multi_thread()
198 .worker_threads(num_threads)
199 .enable_all()
200 .build()?
201 };
202 Ok(rt)
203}