1use std::fmt;
2use std::fmt::Display;
3use std::sync::atomic::{AtomicU64, Ordering};
4use std::time::Instant;
5
6pub fn format_micros(micros: i32, f: &mut dyn fmt::Write) -> fmt::Result {
7 return if micros < 1_000 {
8 write!(f, "{} us", micros)
9 } else if micros < 10_000 {
10 write!(f, "{:.2} ms", micros as f64 / 1_000.)
11 } else if micros < 100_000 {
12 write!(f, "{:.1} ms", micros as f64 / 1_000.)
13 } else if micros < 1_000_000 {
14 write!(f, "{} ms", micros / 1_000)
15 } else if micros < 10_000_000 {
16 write!(f, "{:.2} s", micros as f64 / 1_000_000.)
17 } else if micros < 100_000_000 {
18 write!(f, "{:.1} s", micros as f64 / 1_000_000.)
19 } else {
20 write!(f, "{} s", micros / 1_000_000)
21 };
22}
23
24pub struct Watch {
25 start: Instant
26}
27
28impl Watch {
29 pub fn start() -> Watch {
30 Watch { start: Instant::now() }
31 }
32
33 pub fn micros(&self) -> i32 {
34 self.start.elapsed().as_micros() as i32
35 }
36}
37
38impl Display for Watch {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 let micros = self.micros();
41 format_micros(micros, f)
42 }
43}
44
45pub struct Average {
46 sum_and_count: AtomicU64,
47 count: AtomicU64,
48}
49
50impl Clone for Average {
51 fn clone(&self) -> Self {
52 Average {
53 sum_and_count: AtomicU64::new(self.sum_and_count.load(Ordering::Relaxed)),
54 count: AtomicU64::new(self.count.load(Ordering::Relaxed)),
55 }
56 }
57}
58
59impl Average {
60 pub fn new() -> Average {
61 Average {
62 sum_and_count: AtomicU64::new(0),
63 count: AtomicU64::new(0),
64 }
65 }
66
67 fn sum_and_count(&self) -> (i32, i32) {
68 let last_sum_and_count = self.sum_and_count.load(Ordering::Relaxed);
69 let count = (last_sum_and_count & 0xFFFFFFFF) as i32;
70 let sum = ((last_sum_and_count >> 32) & 0xFFFFFFFF) as i32;
71
72 return (sum, count);
73 }
74
75 pub fn add(&self, value: i32) {
76 self.count.fetch_add(1, Ordering::Relaxed);
77
78 let (mut sum, mut count) = self.sum_and_count();
79
80 while count > 100 || sum as i64 + value as i64 > std::i32::MAX as i64 {
81 sum = count / 2 * sum / count;
82 count = count / 2;
83 }
84
85 sum += value;
86 count += 1;
87
88 let next_sum_and_count = (sum as u64 & 0xFFFFFFFF) << 32 | (count as u64 & 0xFFFFFFFF);
89 self.sum_and_count.store(next_sum_and_count, Ordering::Relaxed);
90 }
91
92 pub fn count(&self) -> u64 {
93 self.count.load(Ordering::Relaxed)
94 }
95
96 pub fn avg(&self) -> i32 {
97 let (sum, count) = self.sum_and_count();
98
99 return if sum == 0 {
100 0
101 } else {
102 sum / count
103 };
104 }
105}
106
107impl Display for Average {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 format_micros(self.avg(), f)?;
110 write!(f, " ({})", self.count())?;
111 Ok(())
112 }
113}
114
115#[cfg(test)]
116mod test {
117 use crate::watch::{Average, format_micros, Watch};
118
119 fn format(micros: i32) -> String {
120 let mut result = String::new();
121 format_micros(micros, &mut result).unwrap_or_default();
122
123 return result;
124 }
125
126 #[test]
127 fn formatting_works_as_expected() {
128 let w = Watch::start();
129 println!("{}", w);
130 assert_eq!(format(1), "1 us");
131 assert_eq!(format(12), "12 us");
132 assert_eq!(format(123), "123 us");
133 assert_eq!(format(1230), "1.23 ms");
134 assert_eq!(format(12300), "12.3 ms");
135 assert_eq!(format(123000), "123 ms");
136 assert_eq!(format(1230000), "1.23 s");
137 assert_eq!(format(12300000), "12.3 s");
138 assert_eq!(format(123000000), "123 s");
139 println!("{}", w);
140 }
141
142 #[test]
143 fn empty_average_is_properly_initialized() {
144 let avg = Average::new();
145 assert_eq!(avg.avg(), 0);
146 assert_eq!(avg.count(), 0);
147 }
148
149 #[test]
150 fn average_with_some_values_works() {
151 let avg = Average::new();
152 for i in 1..=10 { avg.add(i); }
153 assert_eq!(avg.avg(), 5);
154 assert_eq!(avg.count(), 10);
155 }
156
157 #[test]
158 fn formatting_average_works() {
159 let avg = Average::new();
160 avg.add(10_123);
161 assert_eq!(format!("{}", avg), "10.1 ms (1)");
162 }
163
164 #[test]
165 fn average_with_many_values_keeps_count() {
166 let avg = Average::new();
167 for i in 1..=1000 { avg.add(i); }
168 assert_eq!(avg.avg(), 928);
169 assert_eq!(avg.count(), 1000);
170 }
171
172 #[test]
173 fn average_overflows_sanely() {
174 {
175 let avg = Average::new();
176 avg.add(std::i32::MAX);
177 assert_eq!(avg.avg(), std::i32::MAX);
178 avg.add(std::i32::MAX);
179 assert_eq!(avg.avg(), std::i32::MAX);
180 avg.add(std::i32::MAX / 2);
181 avg.add(std::i32::MAX / 2);
182 assert_eq!(avg.avg(), std::i32::MAX / 2);
183 }
184 {
185 let avg = Average::new();
186 avg.add(10);
187 avg.add(std::i32::MAX - 50);
188 avg.add(60);
189
190 let average_before_overflow = (std::i32::MAX - 50 + 10) / 2;
194 let expected_average = (average_before_overflow + 60) / 2;
195 assert_eq!(avg.avg(), expected_average);
196 }
197 }
198}