@@ -7,23 +7,22 @@ use crate::metrics::data::HistogramDataPoint;
77use crate :: metrics:: data:: { self , Aggregation , Temporality } ;
88use opentelemetry:: KeyValue ;
99
10- use super :: Number ;
11- use super :: { AtomicTracker , AtomicallyUpdate , Operation , ValueMap } ;
12-
13- struct HistogramUpdate ;
14-
15- impl Operation for HistogramUpdate {
16- fn update_tracker < T : Default , AT : AtomicTracker < T > > ( tracker : & AT , value : T , index : usize ) {
17- tracker. update_histogram ( index, value) ;
18- }
19- }
10+ use super :: ValueMap ;
11+ use super :: { Aggregator , Number } ;
2012
2113struct HistogramTracker < T > {
2214 buckets : Mutex < Buckets < T > > ,
2315}
2416
25- impl < T : Number > AtomicTracker < T > for HistogramTracker < T > {
26- fn update_histogram ( & self , index : usize , value : T ) {
17+ impl < T > Aggregator < T > for HistogramTracker < T >
18+ where
19+ T : Number ,
20+ {
21+ type InitConfig = usize ;
22+ /// Value and bucket index
23+ type PreComputedValue = ( T , usize ) ;
24+
25+ fn update ( & self , ( value, index) : ( T , usize ) ) {
2726 let mut buckets = match self . buckets . lock ( ) {
2827 Ok ( guard) => guard,
2928 Err ( _) => return ,
@@ -32,15 +31,10 @@ impl<T: Number> AtomicTracker<T> for HistogramTracker<T> {
3231 buckets. bin ( index, value) ;
3332 buckets. sum ( value) ;
3433 }
35- }
36-
37- impl < T : Number > AtomicallyUpdate < T > for HistogramTracker < T > {
38- type AtomicTracker = HistogramTracker < T > ;
3934
40- fn new_atomic_tracker ( buckets_count : Option < usize > ) -> Self :: AtomicTracker {
41- let count = buckets_count. unwrap ( ) ;
35+ fn create ( count : & usize ) -> Self {
4236 HistogramTracker {
43- buckets : Mutex :: new ( Buckets :: < T > :: new ( count) ) ,
37+ buckets : Mutex :: new ( Buckets :: < T > :: new ( * count) ) ,
4438 }
4539 }
4640}
@@ -94,7 +88,7 @@ impl<T: Number> Buckets<T> {
9488/// Summarizes a set of measurements as a histogram with explicitly defined
9589/// buckets.
9690pub ( crate ) struct Histogram < T : Number > {
97- value_map : ValueMap < HistogramTracker < T > , T , HistogramUpdate > ,
91+ value_map : ValueMap < T , HistogramTracker < T > > ,
9892 bounds : Vec < f64 > ,
9993 record_min_max : bool ,
10094 record_sum : bool ,
@@ -103,9 +97,11 @@ pub(crate) struct Histogram<T: Number> {
10397
10498impl < T : Number > Histogram < T > {
10599 pub ( crate ) fn new ( boundaries : Vec < f64 > , record_min_max : bool , record_sum : bool ) -> Self {
100+ // TODO fix the bug, by first removing NaN and only then getting buckets_count
101+ // once we know the reason for performance degradation
106102 let buckets_count = boundaries. len ( ) + 1 ;
107103 let mut histogram = Histogram {
108- value_map : ValueMap :: new_with_buckets_count ( buckets_count) ,
104+ value_map : ValueMap :: new ( buckets_count) ,
109105 bounds : boundaries,
110106 record_min_max,
111107 record_sum,
@@ -122,14 +118,20 @@ impl<T: Number> Histogram<T> {
122118
123119 pub ( crate ) fn measure ( & self , measurement : T , attrs : & [ KeyValue ] ) {
124120 let f = measurement. into_float ( ) ;
125-
121+ // Ignore NaN and infinity.
122+ // Only makes sense if T is f64, maybe this could be no-op for other cases?
123+ // TODO: uncomment once we know the reason for performance degradation
124+ // if f.is_infinite() || f.is_nan() {
125+ // return;
126+ // }
126127 // This search will return an index in the range `[0, bounds.len()]`, where
127128 // it will return `bounds.len()` if value is greater than the last element
128129 // of `bounds`. This aligns with the buckets in that the length of buckets
129130 // is `bounds.len()+1`, with the last bucket representing:
130131 // `(bounds[bounds.len()-1], +∞)`.
131132 let index = self . bounds . partition_point ( |& x| x < f) ;
132- self . value_map . measure ( measurement, attrs, index) ;
133+
134+ self . value_map . measure ( ( measurement, index) , attrs) ;
133135 }
134136
135137 pub ( crate ) fn delta (
@@ -350,3 +352,69 @@ impl<T: Number> Histogram<T> {
350352 ( h. data_points . len ( ) , new_agg. map ( |a| Box :: new ( a) as Box < _ > ) )
351353 }
352354}
355+
356+ // TODO: uncomment once we know the reason for performance degradation
357+ // #[cfg(test)]
358+ // mod tests {
359+
360+ // use super::*;
361+
362+ // #[test]
363+ // fn when_f64_is_nan_or_infinity_then_ignore() {
364+ // struct Expected {
365+ // min: f64,
366+ // max: f64,
367+ // sum: f64,
368+ // count: u64,
369+ // }
370+ // impl Expected {
371+ // fn new(min: f64, max: f64, sum: f64, count: u64) -> Self {
372+ // Expected {
373+ // min,
374+ // max,
375+ // sum,
376+ // count,
377+ // }
378+ // }
379+ // }
380+ // struct TestCase {
381+ // values: Vec<f64>,
382+ // expected: Expected,
383+ // }
384+
385+ // let test_cases = vec![
386+ // TestCase {
387+ // values: vec![2.0, 4.0, 1.0],
388+ // expected: Expected::new(1.0, 4.0, 7.0, 3),
389+ // },
390+ // TestCase {
391+ // values: vec![2.0, 4.0, 1.0, f64::INFINITY],
392+ // expected: Expected::new(1.0, 4.0, 7.0, 3),
393+ // },
394+ // TestCase {
395+ // values: vec![2.0, 4.0, 1.0, -f64::INFINITY],
396+ // expected: Expected::new(1.0, 4.0, 7.0, 3),
397+ // },
398+ // TestCase {
399+ // values: vec![2.0, f64::NAN, 4.0, 1.0],
400+ // expected: Expected::new(1.0, 4.0, 7.0, 3),
401+ // },
402+ // TestCase {
403+ // values: vec![4.0, 4.0, 4.0, 2.0, 16.0, 1.0],
404+ // expected: Expected::new(1.0, 16.0, 31.0, 6),
405+ // },
406+ // ];
407+
408+ // for test in test_cases {
409+ // let h = Histogram::new(vec![], true, true);
410+ // for v in test.values {
411+ // h.measure(v, &[]);
412+ // }
413+ // let res = h.value_map.no_attribute_tracker.buckets.lock().unwrap();
414+ // assert_eq!(test.expected.max, res.max);
415+ // assert_eq!(test.expected.min, res.min);
416+ // assert_eq!(test.expected.sum, res.total);
417+ // assert_eq!(test.expected.count, res.count);
418+ // }
419+ // }
420+ // }
0 commit comments