fast_telemetry/metric/
max_gauge_f64.rs1use crate::thread_id::thread_id;
8use crossbeam_utils::CachePadded;
9use std::fmt;
10use std::sync::atomic::{AtomicU64, Ordering};
11
12const SIGN_MASK: u64 = 1u64 << 63;
13
14#[inline]
15fn encode_ordered_f64(value: f64) -> u64 {
16 let bits = value.to_bits();
17 if bits & SIGN_MASK != 0 {
18 !bits
19 } else {
20 bits ^ SIGN_MASK
21 }
22}
23
24#[inline]
25fn decode_ordered_f64(encoded: u64) -> f64 {
26 let bits = if encoded & SIGN_MASK != 0 {
27 encoded ^ SIGN_MASK
28 } else {
29 !encoded
30 };
31 f64::from_bits(bits)
32}
33
34pub struct MaxGaugeF64 {
38 cells: Vec<CachePadded<AtomicU64>>,
39 reset_value: u64,
40}
41
42impl MaxGaugeF64 {
43 pub fn new(shard_count: usize) -> Self {
45 Self::with_value(shard_count, 0.0)
46 }
47
48 pub fn with_value(shard_count: usize, initial: f64) -> Self {
50 let shard_count = shard_count.next_power_of_two();
51 let reset_value = encode_ordered_f64(initial);
52 Self {
53 cells: (0..shard_count)
54 .map(|_| CachePadded::new(AtomicU64::new(reset_value)))
55 .collect(),
56 reset_value,
57 }
58 }
59
60 #[inline]
64 pub fn observe(&self, value: f64) {
65 if value.is_nan() {
66 return;
67 }
68 let idx = thread_id() & (self.cells.len() - 1);
69 let cell = if cfg!(debug_assertions) {
70 self.cells.get(idx).expect("index out of bounds")
71 } else {
72 unsafe { self.cells.get_unchecked(idx) }
73 };
74 cell.fetch_max(encode_ordered_f64(value), Ordering::Relaxed);
75 }
76
77 #[inline]
79 pub fn get(&self) -> f64 {
80 decode_ordered_f64(
81 self.cells
82 .iter()
83 .map(|cell| cell.load(Ordering::Relaxed))
84 .max()
85 .unwrap_or(self.reset_value),
86 )
87 }
88
89 pub fn reset(&self) {
91 for cell in &self.cells {
92 cell.store(self.reset_value, Ordering::Relaxed);
93 }
94 }
95
96 pub fn swap_reset(&self) -> f64 {
98 decode_ordered_f64(
99 self.cells
100 .iter()
101 .map(|cell| cell.swap(self.reset_value, Ordering::Relaxed))
102 .max()
103 .unwrap_or(self.reset_value),
104 )
105 }
106}
107
108impl Default for MaxGaugeF64 {
109 fn default() -> Self {
110 Self::new(4)
111 }
112}
113
114impl fmt::Debug for MaxGaugeF64 {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 f.debug_struct("MaxGaugeF64")
117 .field("max", &self.get())
118 .field("cells", &self.cells.len())
119 .finish()
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn basic_observe() {
129 let gauge = MaxGaugeF64::new(4);
130 gauge.observe(3.5);
131 gauge.observe(7.25);
132 gauge.observe(5.0);
133 assert!((gauge.get() - 7.25).abs() < f64::EPSILON);
134 }
135
136 #[test]
137 fn negative_values_order_correctly() {
138 let gauge = MaxGaugeF64::with_value(4, -10.0);
139 gauge.observe(-3.25);
140 gauge.observe(-8.0);
141 assert!((gauge.get() - (-3.25)).abs() < f64::EPSILON);
142 }
143
144 #[test]
145 fn nan_is_ignored() {
146 let gauge = MaxGaugeF64::with_value(4, 1.0);
147 gauge.observe(f64::NAN);
148 assert!((gauge.get() - 1.0).abs() < f64::EPSILON);
149 }
150}