1use print_duration::print_duration;
2
3use std::{
4 convert::TryInto,
5 io::{stdout, Write},
6 sync::{
7 atomic::{AtomicBool, AtomicU64, AtomicU8, Ordering},
8 Arc, Mutex,
9 },
10 thread,
11 time::{Duration, Instant},
12};
13
14pub fn update_execution_position<const N: usize>(
15 i: usize,
16 execution_position_timer: Instant,
17 thread_execution_position: &Arc<AtomicU8>,
18 thread_execution_times: &Arc<[Mutex<(Duration, u64)>; N]>,
19) -> Instant {
20 {
21 let mut data = thread_execution_times[i - 1].lock().unwrap();
22 data.0 += execution_position_timer.elapsed();
23 data.1 += 1;
24 }
25 thread_execution_position.store(i as u8, Ordering::SeqCst);
26 Instant::now()
27}
28
29pub struct Polling {
31 pub poll_rate: Duration,
33 pub printing: bool,
35 pub early_exit_minimum: Option<f64>,
37 pub thread_execution_reporting: bool,
39}
40impl Polling {
41 const DEFAULT_POLL_RATE: Duration = Duration::from_millis(10);
42 pub fn new(printing: bool, early_exit_minimum: Option<f64>) -> Self {
44 Self {
45 poll_rate: Polling::DEFAULT_POLL_RATE,
46 printing,
47 early_exit_minimum,
48 thread_execution_reporting: false,
49 }
50 }
51}
52impl Default for Polling {
53 fn default() -> Self {
63 Self {
64 poll_rate: Polling::DEFAULT_POLL_RATE,
65 printing: true,
66 early_exit_minimum: None,
67 thread_execution_reporting: false,
68 }
69 }
70}
71
72pub fn poll<const N: usize>(
73 data: Polling,
74 counters: Vec<Arc<AtomicU64>>,
76 offset: u64,
77 iterations: u64,
79 thread_bests: Vec<Arc<Mutex<f64>>>,
81 thread_exit: Arc<AtomicBool>,
83 thread_execution_positions: Vec<Arc<AtomicU8>>,
85 thread_execution_times: Vec<Arc<[Mutex<(Duration, u64)>; N]>>,
87) {
88 let start = Instant::now();
89 let mut stdout = stdout();
90 let mut count = offset
91 + counters
92 .iter()
93 .map(|c| c.load(Ordering::SeqCst))
94 .sum::<u64>();
95
96 if data.printing {
97 println!("{:20}", iterations);
98 }
99
100 let mut poll_time = Instant::now();
101 let mut held_best: f64 = f64::MAX;
102
103 let mut held_average_execution_times: [(Duration, u64); N] =
104 vec![(Duration::new(0, 0), 0); N].try_into().unwrap();
105 let mut held_recent_execution_times: [Duration; N] =
106 vec![Duration::new(0, 0); N].try_into().unwrap();
107 while count < iterations {
108 if data.printing {
109 let percent = count as f32 / iterations as f32;
111
112 let remaining_time_estimate = if count == 0 {
114 Duration::new(0, 0)
115 } else {
116 start.elapsed().div_f32(percent)
117 };
118 print!(
119 "\r{:20} ({:.2}%) {} / {} [{}] {}\t",
120 count,
121 100. * percent,
122 print_duration(start.elapsed(), 0..3),
123 print_duration(remaining_time_estimate, 0..3),
124 if held_best == f64::MAX {
125 String::from("?")
126 } else {
127 format!("{}", held_best)
128 },
129 if data.thread_execution_reporting {
130 let (average_execution_times, recent_execution_times): (
131 Vec<String>,
132 Vec<String>,
133 ) = (0..thread_execution_times[0].len())
134 .map(|i| {
135 let (mut sum, mut num) = (Duration::new(0, 0), 0);
136 for n in 0..thread_execution_times.len() {
137 {
138 let mut data = thread_execution_times[n][i].lock().unwrap();
139 sum += data.0;
140 held_average_execution_times[i].0 += data.0;
141 num += data.1;
142 held_average_execution_times[i].1 += data.1;
143 *data = (Duration::new(0, 0), 0);
144 }
145 }
146 if num > 0 {
147 held_recent_execution_times[i] = sum.div_f64(num as f64);
148 }
149 (
150 if held_average_execution_times[i].1 > 0 {
151 format!(
152 "{:.1?}",
153 held_average_execution_times[i]
154 .0
155 .div_f64(held_average_execution_times[i].1 as f64)
156 )
157 } else {
158 String::from("?")
159 },
160 if held_recent_execution_times[i] > Duration::new(0, 0) {
161 format!("{:.1?}", held_recent_execution_times[i])
162 } else {
163 String::from("?")
164 },
165 )
166 })
167 .unzip();
168
169 let execution_positions: Vec<u8> = thread_execution_positions
170 .iter()
171 .map(|pos| pos.load(Ordering::SeqCst))
172 .collect();
173 format!(
174 "{{ [{}] [{}] {:.?} }}",
175 recent_execution_times.join(", "),
176 average_execution_times.join(", "),
177 execution_positions
178 )
179 } else {
180 String::from("")
181 }
182 );
183 stdout.flush().unwrap();
184 }
185
186 match (data.early_exit_minimum, data.printing) {
188 (Some(early_exit), true) => {
189 for thread_best in thread_bests.iter() {
190 let thread_best_temp = *thread_best.lock().unwrap();
191 if thread_best_temp < held_best {
192 held_best = thread_best_temp;
193 if thread_best_temp <= early_exit {
194 thread_exit.store(true, Ordering::SeqCst);
195 println!();
196 return;
197 }
198 }
199 }
200 }
201 (None, true) => {
202 for thread_best in thread_bests.iter() {
203 let thread_best_temp = *thread_best.lock().unwrap();
204 if thread_best_temp < held_best {
205 held_best = thread_best_temp;
206 }
207 }
208 }
209 (Some(early_exit), false) => {
210 for thread_best in thread_bests.iter() {
211 if *thread_best.lock().unwrap() <= early_exit {
212 thread_exit.store(true, Ordering::SeqCst);
213 return;
214 }
215 }
216 }
217 (None, false) => {}
218 }
219
220 thread::sleep(saturating_sub(data.poll_rate, poll_time.elapsed()));
221 poll_time = Instant::now();
222
223 count = offset
224 + counters
225 .iter()
226 .map(|c| c.load(Ordering::SeqCst))
227 .sum::<u64>();
228 }
229
230 if data.printing {
231 println!(
232 "\r{:20} (100.00%) {} / {} [{}] {}\t",
233 count,
234 print_duration(start.elapsed(), 0..3),
235 print_duration(start.elapsed(), 0..3),
236 held_best,
237 if data.thread_execution_reporting {
238 let (average_execution_times, recent_execution_times): (Vec<String>, Vec<String>) =
239 (0..thread_execution_times[0].len())
240 .map(|i| {
241 let (mut sum, mut num) = (Duration::new(0, 0), 0);
242 for n in 0..thread_execution_times.len() {
243 {
244 let mut data = thread_execution_times[n][i].lock().unwrap();
245 sum += data.0;
246 held_average_execution_times[i].0 += data.0;
247 num += data.1;
248 held_average_execution_times[i].1 += data.1;
249 *data = (Duration::new(0, 0), 0);
250 }
251 }
252 if num > 0 {
253 held_recent_execution_times[i] = sum.div_f64(num as f64);
254 }
255 (
256 if held_average_execution_times[i].1 > 0 {
257 format!(
258 "{:.1?}",
259 held_average_execution_times[i]
260 .0
261 .div_f64(held_average_execution_times[i].1 as f64)
262 )
263 } else {
264 String::from("?")
265 },
266 if held_recent_execution_times[i] > Duration::new(0, 0) {
267 format!("{:.1?}", held_recent_execution_times[i])
268 } else {
269 String::from("?")
270 },
271 )
272 })
273 .unzip();
274
275 let execution_positions: Vec<u8> = thread_execution_positions
276 .iter()
277 .map(|pos| pos.load(Ordering::SeqCst))
278 .collect();
279 format!(
280 "{{ [{}] [{}] {:.?} }}",
281 recent_execution_times.join(", "),
282 average_execution_times.join(", "),
283 execution_positions
284 )
285 } else {
286 String::from("")
287 }
288 );
289 stdout.flush().unwrap();
290 }
291}
292fn saturating_sub(a: Duration, b: Duration) -> Duration {
294 if let Some(dur) = a.checked_sub(b) {
295 dur
296 } else {
297 Duration::new(0, 0)
298 }
299}