@@ -31,6 +31,23 @@ pub struct Histogram {
31
31
config : Config ,
32
32
}
33
33
34
+ /// Snapshot is a structure that contains histogram statistics such as
35
+ /// min, max, mean, standard deviation, median, and most common percentiles
36
+ /// collected in a certain moment.
37
+ #[ derive( Debug ) ]
38
+ pub struct Snapshot {
39
+ pub min : u64 ,
40
+ pub max : u64 ,
41
+ pub mean : u64 ,
42
+ pub stddev : u64 ,
43
+ pub median : u64 ,
44
+ pub percentile_75 : u64 ,
45
+ pub percentile_90 : u64 ,
46
+ pub percentile_95 : u64 ,
47
+ pub percentile_99 : u64 ,
48
+ pub percentile_99_9 : u64 ,
49
+ }
50
+
34
51
impl Histogram {
35
52
pub fn new ( ) -> Self {
36
53
let grouping_power = 7 ;
@@ -109,6 +126,95 @@ impl Histogram {
109
126
}
110
127
}
111
128
129
+ pub fn snapshot ( ) -> impl FnOnce ( & [ AtomicU64 ] , & Config ) -> Result < Snapshot , & ' static str > {
130
+ |buckets, config| {
131
+ let total_count = Histogram :: get_total_count ( buckets) ;
132
+
133
+ let mut min = u64:: MAX ;
134
+ let mut max = 0 ;
135
+ let mut weighted_sum = 0 ;
136
+ let mut pref_sum = 0 ;
137
+ let mut percentile_75 = 0 ;
138
+ let mut percentile_90 = 0 ;
139
+ let mut percentile_95 = 0 ;
140
+ let mut percentile_99 = 0 ;
141
+ let mut percentile_99_9 = 0 ;
142
+
143
+ let percentile_75_threshold = ( 0.75 * total_count as f64 ) . ceil ( ) as u128 ;
144
+ let percentile_90_threshold = ( 0.9 * total_count as f64 ) . ceil ( ) as u128 ;
145
+ let percentile_95_threshold = ( 0.95 * total_count as f64 ) . ceil ( ) as u128 ;
146
+ let percentile_99_threshold = ( 0.99 * total_count as f64 ) . ceil ( ) as u128 ;
147
+ let percentile_99_9_threshold = ( 0.999 * total_count as f64 ) . ceil ( ) as u128 ;
148
+
149
+ for ( i, bucket) in buckets. iter ( ) . enumerate ( ) {
150
+ let count = bucket. load ( ORDER_TYPE ) as u128 ;
151
+ if count == 0 {
152
+ continue ;
153
+ }
154
+
155
+ let lower_bound = config. index_to_lower_bound ( i) ;
156
+ let upper_bound = config. index_to_upper_bound ( i) ;
157
+
158
+ if lower_bound < min {
159
+ min = lower_bound;
160
+ }
161
+ if upper_bound > max {
162
+ max = upper_bound;
163
+ }
164
+
165
+ weighted_sum += count * lower_bound as u128 ;
166
+
167
+ let next_pref_sum = pref_sum + count;
168
+ if pref_sum < percentile_75_threshold && next_pref_sum >= percentile_75_threshold {
169
+ percentile_75 = lower_bound;
170
+ }
171
+ if pref_sum < percentile_90_threshold && next_pref_sum >= percentile_90_threshold {
172
+ percentile_90 = lower_bound;
173
+ }
174
+ if pref_sum < percentile_95_threshold && next_pref_sum >= percentile_95_threshold {
175
+ percentile_95 = lower_bound;
176
+ }
177
+ if pref_sum < percentile_99_threshold && next_pref_sum >= percentile_99_threshold {
178
+ percentile_99 = lower_bound;
179
+ }
180
+ if pref_sum < percentile_99_9_threshold
181
+ && next_pref_sum >= percentile_99_9_threshold
182
+ {
183
+ percentile_99_9 = lower_bound;
184
+ }
185
+
186
+ pref_sum = next_pref_sum;
187
+ }
188
+
189
+ let mean = ( weighted_sum / total_count) as u64 ;
190
+ let mut variance_sum = 0 ;
191
+ for ( i, bucket) in buckets. iter ( ) . enumerate ( ) {
192
+ let count = bucket. load ( ORDER_TYPE ) as u128 ;
193
+ if count == 0 {
194
+ continue ;
195
+ }
196
+
197
+ let lower_bound = config. index_to_lower_bound ( i) ;
198
+ variance_sum += count * ( lower_bound as u128 - mean as u128 ) . pow ( 2 ) ;
199
+ }
200
+ let variance = variance_sum / total_count;
201
+ let stddev = ( variance as f64 ) . sqrt ( ) as u64 ;
202
+
203
+ Ok ( Snapshot {
204
+ min,
205
+ max,
206
+ mean,
207
+ stddev,
208
+ median : config. index_to_lower_bound ( buckets. len ( ) / 2 ) ,
209
+ percentile_75,
210
+ percentile_90,
211
+ percentile_95,
212
+ percentile_99,
213
+ percentile_99_9,
214
+ } )
215
+ }
216
+ }
217
+
112
218
pub fn get_total_count ( buckets : & [ AtomicU64 ] ) -> u128 {
113
219
buckets. iter ( ) . map ( |v| v. load ( ORDER_TYPE ) as u128 ) . sum ( )
114
220
}
0 commit comments