1 /**
2 * hdr_histogram.c
3 * Written by Michael Barker and released to the public domain,
4 * as explained at http://creativecommons.org/publicdomain/zero/1.0/
5 */
6
7 #include <stdlib.h>
8 #include <stdbool.h>
9 #include <math.h>
10 #include <assert.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdint.h>
14 #include <errno.h>
15 #include <inttypes.h>
16
17 #include "hdr_histogram.h"
18 #include "hdr_tests.h"
19
20 /* ###### ####### ## ## ## ## ######## ###### */
21 /* ## ## ## ## ## ## ### ## ## ## ## */
22 /* ## ## ## ## ## #### ## ## ## */
23 /* ## ## ## ## ## ## ## ## ## ###### */
24 /* ## ## ## ## ## ## #### ## ## */
25 /* ## ## ## ## ## ## ## ### ## ## ## */
26 /* ###### ####### ####### ## ## ## ###### */
27
normalize_index(const struct hdr_histogram * h,int32_t index)28 static int32_t normalize_index(const struct hdr_histogram* h, int32_t index)
29 {
30 int32_t normalized_index;
31 int32_t adjustment = 0;
32 if (h->normalizing_index_offset == 0)
33 {
34 return index;
35 }
36
37 normalized_index = index - h->normalizing_index_offset;
38
39 if (normalized_index < 0)
40 {
41 adjustment = h->counts_len;
42 }
43 else if (normalized_index >= h->counts_len)
44 {
45 adjustment = -h->counts_len;
46 }
47
48 return normalized_index + adjustment;
49 }
50
counts_get_direct(const struct hdr_histogram * h,int32_t index)51 static int64_t counts_get_direct(const struct hdr_histogram* h, int32_t index)
52 {
53 return h->counts[index];
54 }
55
counts_get_normalised(const struct hdr_histogram * h,int32_t index)56 static int64_t counts_get_normalised(const struct hdr_histogram* h, int32_t index)
57 {
58 return counts_get_direct(h, normalize_index(h, index));
59 }
60
counts_inc_normalised(struct hdr_histogram * h,int32_t index,int64_t value)61 static void counts_inc_normalised(
62 struct hdr_histogram* h, int32_t index, int64_t value)
63 {
64 int32_t normalised_index = normalize_index(h, index);
65 h->counts[normalised_index] += value;
66 h->total_count += value;
67 }
68
update_min_max(struct hdr_histogram * h,int64_t value)69 static void update_min_max(struct hdr_histogram* h, int64_t value)
70 {
71 h->min_value = (value < h->min_value && value != 0) ? value : h->min_value;
72 h->max_value = (value > h->max_value) ? value : h->max_value;
73 }
74
75 /* ## ## ######## #### ## #### ######## ## ## */
76 /* ## ## ## ## ## ## ## ## ## */
77 /* ## ## ## ## ## ## ## #### */
78 /* ## ## ## ## ## ## ## ## */
79 /* ## ## ## ## ## ## ## ## */
80 /* ## ## ## ## ## ## ## ## */
81 /* ####### ## #### ######## #### ## ## */
82
power(int64_t base,int64_t exp)83 static int64_t power(int64_t base, int64_t exp)
84 {
85 int64_t result = 1;
86 while(exp)
87 {
88 result *= base; exp--;
89 }
90 return result;
91 }
92
93 #if defined(_MSC_VER)
94 # if defined(_WIN64)
95 # pragma intrinsic(_BitScanReverse64)
96 # else
97 # pragma intrinsic(_BitScanReverse)
98 # endif
99 #endif
100
get_bucket_index(const struct hdr_histogram * h,int64_t value)101 static int32_t get_bucket_index(const struct hdr_histogram* h, int64_t value)
102 {
103 #if defined(_MSC_VER)
104 uint32_t leading_zero = 0;
105 int64_t masked_value = value | h->sub_bucket_mask;
106 # if defined(_WIN64)
107 _BitScanReverse64(&leading_zero, masked_value);
108 # else
109 uint32_t high = masked_value >> 32;
110 if (_BitScanReverse(&leading_zero, high)) {
111 leading_zero += 32;
112 } else {
113 uint32_t low = masked_value & 0x00000000FFFFFFFF;
114 _BitScanReverse(&leading_zero, low);
115 }
116 # endif
117 int32_t pow2ceiling = 64 - (63 - leading_zero); /* smallest power of 2 containing value */
118 #else
119 int32_t pow2ceiling = 64 - __builtin_clzll(value | h->sub_bucket_mask); /* smallest power of 2 containing value */
120 #endif
121 return pow2ceiling - h->unit_magnitude - (h->sub_bucket_half_count_magnitude + 1);
122 }
123
get_sub_bucket_index(int64_t value,int32_t bucket_index,int32_t unit_magnitude)124 static int32_t get_sub_bucket_index(int64_t value, int32_t bucket_index, int32_t unit_magnitude)
125 {
126 return (int32_t)(value >> (bucket_index + unit_magnitude));
127 }
128
counts_index(const struct hdr_histogram * h,int32_t bucket_index,int32_t sub_bucket_index)129 static int32_t counts_index(const struct hdr_histogram* h, int32_t bucket_index, int32_t sub_bucket_index)
130 {
131 /* Calculate the index for the first entry in the bucket: */
132 /* (The following is the equivalent of ((bucket_index + 1) * subBucketHalfCount) ): */
133 int32_t bucket_base_index = (bucket_index + 1) << h->sub_bucket_half_count_magnitude;
134 /* Calculate the offset in the bucket: */
135 int32_t offset_in_bucket = sub_bucket_index - h->sub_bucket_half_count;
136 /* The following is the equivalent of ((sub_bucket_index - subBucketHalfCount) + bucketBaseIndex; */
137 return bucket_base_index + offset_in_bucket;
138 }
139
value_from_index(int32_t bucket_index,int32_t sub_bucket_index,int32_t unit_magnitude)140 static int64_t value_from_index(int32_t bucket_index, int32_t sub_bucket_index, int32_t unit_magnitude)
141 {
142 return ((int64_t) sub_bucket_index) << (bucket_index + unit_magnitude);
143 }
144
counts_index_for(const struct hdr_histogram * h,int64_t value)145 int32_t counts_index_for(const struct hdr_histogram* h, int64_t value)
146 {
147 int32_t bucket_index = get_bucket_index(h, value);
148 int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude);
149
150 return counts_index(h, bucket_index, sub_bucket_index);
151 }
152
hdr_value_at_index(const struct hdr_histogram * h,int32_t index)153 int64_t hdr_value_at_index(const struct hdr_histogram *h, int32_t index)
154 {
155 int32_t bucket_index = (index >> h->sub_bucket_half_count_magnitude) - 1;
156 int32_t sub_bucket_index = (index & (h->sub_bucket_half_count - 1)) + h->sub_bucket_half_count;
157
158 if (bucket_index < 0)
159 {
160 sub_bucket_index -= h->sub_bucket_half_count;
161 bucket_index = 0;
162 }
163
164 return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude);
165 }
166
hdr_size_of_equivalent_value_range(const struct hdr_histogram * h,int64_t value)167 int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value)
168 {
169 int32_t bucket_index = get_bucket_index(h, value);
170 int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude);
171 int32_t adjusted_bucket = (sub_bucket_index >= h->sub_bucket_count) ? (bucket_index + 1) : bucket_index;
172 return INT64_C(1) << (h->unit_magnitude + adjusted_bucket);
173 }
174
lowest_equivalent_value(const struct hdr_histogram * h,int64_t value)175 static int64_t lowest_equivalent_value(const struct hdr_histogram* h, int64_t value)
176 {
177 int32_t bucket_index = get_bucket_index(h, value);
178 int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude);
179 return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude);
180 }
181
hdr_next_non_equivalent_value(const struct hdr_histogram * h,int64_t value)182 int64_t hdr_next_non_equivalent_value(const struct hdr_histogram *h, int64_t value)
183 {
184 return lowest_equivalent_value(h, value) + hdr_size_of_equivalent_value_range(h, value);
185 }
186
highest_equivalent_value(const struct hdr_histogram * h,int64_t value)187 static int64_t highest_equivalent_value(const struct hdr_histogram* h, int64_t value)
188 {
189 return hdr_next_non_equivalent_value(h, value) - 1;
190 }
191
hdr_median_equivalent_value(const struct hdr_histogram * h,int64_t value)192 int64_t hdr_median_equivalent_value(const struct hdr_histogram *h, int64_t value)
193 {
194 return lowest_equivalent_value(h, value) + (hdr_size_of_equivalent_value_range(h, value) >> 1);
195 }
196
non_zero_min(const struct hdr_histogram * h)197 static int64_t non_zero_min(const struct hdr_histogram* h)
198 {
199 if (INT64_MAX == h->min_value)
200 {
201 return INT64_MAX;
202 }
203
204 return lowest_equivalent_value(h, h->min_value);
205 }
206
hdr_reset_internal_counters(struct hdr_histogram * h)207 void hdr_reset_internal_counters(struct hdr_histogram* h)
208 {
209 int min_non_zero_index = -1;
210 int max_index = -1;
211 int64_t observed_total_count = 0;
212 int i;
213
214 for (i = 0; i < h->counts_len; i++)
215 {
216 int64_t count_at_index;
217
218 if ((count_at_index = counts_get_direct(h, i)) > 0)
219 {
220 observed_total_count += count_at_index;
221 max_index = i;
222 if (min_non_zero_index == -1 && i != 0)
223 {
224 min_non_zero_index = i;
225 }
226 }
227 }
228
229 if (max_index == -1)
230 {
231 h->max_value = 0;
232 }
233 else
234 {
235 int64_t max_value = hdr_value_at_index(h, max_index);
236 h->max_value = highest_equivalent_value(h, max_value);
237 }
238
239 if (min_non_zero_index == -1)
240 {
241 h->min_value = INT64_MAX;
242 }
243 else
244 {
245 h->min_value = hdr_value_at_index(h, min_non_zero_index);
246 }
247
248 h->total_count = observed_total_count;
249 }
250
buckets_needed_to_cover_value(int64_t value,int32_t sub_bucket_count,int32_t unit_magnitude)251 static int32_t buckets_needed_to_cover_value(int64_t value, int32_t sub_bucket_count, int32_t unit_magnitude)
252 {
253 int64_t smallest_untrackable_value = ((int64_t) sub_bucket_count) << unit_magnitude;
254 int32_t buckets_needed = 1;
255 while (smallest_untrackable_value <= value)
256 {
257 if (smallest_untrackable_value > INT64_MAX / 2)
258 {
259 return buckets_needed + 1;
260 }
261 smallest_untrackable_value <<= 1;
262 buckets_needed++;
263 }
264
265 return buckets_needed;
266 }
267
268 /* ## ## ######## ## ## ####### ######## ## ## */
269 /* ### ### ## ### ### ## ## ## ## ## ## */
270 /* #### #### ## #### #### ## ## ## ## #### */
271 /* ## ### ## ###### ## ### ## ## ## ######## ## */
272 /* ## ## ## ## ## ## ## ## ## ## */
273 /* ## ## ## ## ## ## ## ## ## ## */
274 /* ## ## ######## ## ## ####### ## ## ## */
275
hdr_calculate_bucket_config(int64_t lowest_trackable_value,int64_t highest_trackable_value,int significant_figures,struct hdr_histogram_bucket_config * cfg)276 int hdr_calculate_bucket_config(
277 int64_t lowest_trackable_value,
278 int64_t highest_trackable_value,
279 int significant_figures,
280 struct hdr_histogram_bucket_config* cfg)
281 {
282 int32_t sub_bucket_count_magnitude;
283 int64_t largest_value_with_single_unit_resolution;
284
285 if (lowest_trackable_value < 1 ||
286 significant_figures < 1 || 5 < significant_figures)
287 {
288 return EINVAL;
289 }
290 else if (lowest_trackable_value * 2 > highest_trackable_value)
291 {
292 return EINVAL;
293 }
294
295 cfg->lowest_trackable_value = lowest_trackable_value;
296 cfg->significant_figures = significant_figures;
297 cfg->highest_trackable_value = highest_trackable_value;
298
299 largest_value_with_single_unit_resolution = 2 * power(10, significant_figures);
300 sub_bucket_count_magnitude = (int32_t) ceil(log((double)largest_value_with_single_unit_resolution) / log(2));
301 cfg->sub_bucket_half_count_magnitude = ((sub_bucket_count_magnitude > 1) ? sub_bucket_count_magnitude : 1) - 1;
302
303 cfg->unit_magnitude = (int32_t) floor(log((double)lowest_trackable_value) / log(2));
304
305 cfg->sub_bucket_count = (int32_t) pow(2, (cfg->sub_bucket_half_count_magnitude + 1));
306 cfg->sub_bucket_half_count = cfg->sub_bucket_count / 2;
307 cfg->sub_bucket_mask = ((int64_t) cfg->sub_bucket_count - 1) << cfg->unit_magnitude;
308
309 if (cfg->unit_magnitude + cfg->sub_bucket_half_count_magnitude > 61)
310 {
311 return EINVAL;
312 }
313
314 cfg->bucket_count = buckets_needed_to_cover_value(highest_trackable_value, cfg->sub_bucket_count, (int32_t)cfg->unit_magnitude);
315 cfg->counts_len = (cfg->bucket_count + 1) * (cfg->sub_bucket_count / 2);
316
317 return 0;
318 }
319
hdr_init_preallocated(struct hdr_histogram * h,struct hdr_histogram_bucket_config * cfg)320 void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg)
321 {
322 h->lowest_trackable_value = cfg->lowest_trackable_value;
323 h->highest_trackable_value = cfg->highest_trackable_value;
324 h->unit_magnitude = (int32_t)cfg->unit_magnitude;
325 h->significant_figures = (int32_t)cfg->significant_figures;
326 h->sub_bucket_half_count_magnitude = cfg->sub_bucket_half_count_magnitude;
327 h->sub_bucket_half_count = cfg->sub_bucket_half_count;
328 h->sub_bucket_mask = cfg->sub_bucket_mask;
329 h->sub_bucket_count = cfg->sub_bucket_count;
330 h->min_value = INT64_MAX;
331 h->max_value = 0;
332 h->normalizing_index_offset = 0;
333 h->conversion_ratio = 1.0;
334 h->bucket_count = cfg->bucket_count;
335 h->counts_len = cfg->counts_len;
336 h->total_count = 0;
337 }
338
hdr_init(int64_t lowest_trackable_value,int64_t highest_trackable_value,int significant_figures,struct hdr_histogram ** result)339 int hdr_init(
340 int64_t lowest_trackable_value,
341 int64_t highest_trackable_value,
342 int significant_figures,
343 struct hdr_histogram** result)
344 {
345 int64_t* counts;
346 struct hdr_histogram_bucket_config cfg;
347 struct hdr_histogram* histogram;
348
349 int r = hdr_calculate_bucket_config(lowest_trackable_value, highest_trackable_value, significant_figures, &cfg);
350 if (r)
351 {
352 return r;
353 }
354
355 counts = calloc((size_t) cfg.counts_len, sizeof(int64_t));
356 histogram = calloc(1, sizeof(struct hdr_histogram));
357
358 if (!counts || !histogram)
359 {
360 return ENOMEM;
361 }
362
363 histogram->counts = counts;
364
365 hdr_init_preallocated(histogram, &cfg);
366 *result = histogram;
367
368 return 0;
369 }
370
hdr_close(struct hdr_histogram * h)371 void hdr_close(struct hdr_histogram* h)
372 {
373 free(h->counts);
374 free(h);
375 }
376
hdr_alloc(int64_t highest_trackable_value,int significant_figures,struct hdr_histogram ** result)377 int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result)
378 {
379 return hdr_init(1, highest_trackable_value, significant_figures, result);
380 }
381
382 /* reset a histogram to zero. */
hdr_reset(struct hdr_histogram * h)383 void hdr_reset(struct hdr_histogram *h)
384 {
385 h->total_count=0;
386 h->min_value = INT64_MAX;
387 h->max_value = 0;
388 memset(h->counts, 0, (sizeof(int64_t) * h->counts_len));
389 }
390
hdr_get_memory_size(struct hdr_histogram * h)391 size_t hdr_get_memory_size(struct hdr_histogram *h)
392 {
393 return sizeof(struct hdr_histogram) + h->counts_len * sizeof(int64_t);
394 }
395
396 /* ## ## ######## ######## ### ######## ######## ###### */
397 /* ## ## ## ## ## ## ## ## ## ## ## ## */
398 /* ## ## ## ## ## ## ## ## ## ## ## */
399 /* ## ## ######## ## ## ## ## ## ###### ###### */
400 /* ## ## ## ## ## ######### ## ## ## */
401 /* ## ## ## ## ## ## ## ## ## ## ## */
402 /* ####### ## ######## ## ## ## ######## ###### */
403
404
hdr_record_value(struct hdr_histogram * h,int64_t value)405 bool hdr_record_value(struct hdr_histogram* h, int64_t value)
406 {
407 return hdr_record_values(h, value, 1);
408 }
409
hdr_record_values(struct hdr_histogram * h,int64_t value,int64_t count)410 bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count)
411 {
412 int32_t counts_index;
413
414 if (value < 0)
415 {
416 return false;
417 }
418
419 counts_index = counts_index_for(h, value);
420
421 if (counts_index < 0 || h->counts_len <= counts_index)
422 {
423 return false;
424 }
425
426 counts_inc_normalised(h, counts_index, count);
427 update_min_max(h, value);
428
429 return true;
430 }
431
hdr_record_corrected_value(struct hdr_histogram * h,int64_t value,int64_t expected_interval)432 bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expected_interval)
433 {
434 return hdr_record_corrected_values(h, value, 1, expected_interval);
435 }
436
437
hdr_record_corrected_values(struct hdr_histogram * h,int64_t value,int64_t count,int64_t expected_interval)438 bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval)
439 {
440 int64_t missing_value;
441
442 if (!hdr_record_values(h, value, count))
443 {
444 return false;
445 }
446
447 if (expected_interval <= 0 || value <= expected_interval)
448 {
449 return true;
450 }
451
452 missing_value = value - expected_interval;
453 for (; missing_value >= expected_interval; missing_value -= expected_interval)
454 {
455 if (!hdr_record_values(h, missing_value, count))
456 {
457 return false;
458 }
459 }
460
461 return true;
462 }
463
hdr_add(struct hdr_histogram * h,const struct hdr_histogram * from)464 int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from)
465 {
466 struct hdr_iter iter;
467 int64_t dropped = 0;
468 hdr_iter_recorded_init(&iter, from);
469
470 while (hdr_iter_next(&iter))
471 {
472 int64_t value = iter.value;
473 int64_t count = iter.count;
474
475 if (!hdr_record_values(h, value, count))
476 {
477 dropped += count;
478 }
479 }
480
481 return dropped;
482 }
483
hdr_add_while_correcting_for_coordinated_omission(struct hdr_histogram * h,struct hdr_histogram * from,int64_t expected_interval)484 int64_t hdr_add_while_correcting_for_coordinated_omission(
485 struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval)
486 {
487 struct hdr_iter iter;
488 int64_t dropped = 0;
489 hdr_iter_recorded_init(&iter, from);
490
491 while (hdr_iter_next(&iter))
492 {
493 int64_t value = iter.value;
494 int64_t count = iter.count;
495
496 if (!hdr_record_corrected_values(h, value, count, expected_interval))
497 {
498 dropped += count;
499 }
500 }
501
502 return dropped;
503 }
504
505
506
507 /* ## ## ### ## ## ## ######## ###### */
508 /* ## ## ## ## ## ## ## ## ## ## */
509 /* ## ## ## ## ## ## ## ## ## */
510 /* ## ## ## ## ## ## ## ###### ###### */
511 /* ## ## ######### ## ## ## ## ## */
512 /* ## ## ## ## ## ## ## ## ## ## */
513 /* ### ## ## ######## ####### ######## ###### */
514
515
hdr_max(const struct hdr_histogram * h)516 int64_t hdr_max(const struct hdr_histogram* h)
517 {
518 if (0 == h->max_value)
519 {
520 return 0;
521 }
522
523 return highest_equivalent_value(h, h->max_value);
524 }
525
hdr_min(const struct hdr_histogram * h)526 int64_t hdr_min(const struct hdr_histogram* h)
527 {
528 if (0 < hdr_count_at_index(h, 0))
529 {
530 return 0;
531 }
532
533 return non_zero_min(h);
534 }
535
hdr_value_at_percentile(const struct hdr_histogram * h,double percentile)536 int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile)
537 {
538 struct hdr_iter iter;
539 int64_t total = 0;
540 double requested_percentile = percentile < 100.0 ? percentile : 100.0;
541 int64_t count_at_percentile =
542 (int64_t) (((requested_percentile / 100) * h->total_count) + 0.5);
543 count_at_percentile = count_at_percentile > 1 ? count_at_percentile : 1;
544
545 hdr_iter_init(&iter, h);
546
547 while (hdr_iter_next(&iter))
548 {
549 total += iter.count;
550
551 if (total >= count_at_percentile)
552 {
553 int64_t value_from_index = iter.value;
554 return highest_equivalent_value(h, value_from_index);
555 }
556 }
557
558 return 0;
559 }
560
hdr_mean(const struct hdr_histogram * h)561 double hdr_mean(const struct hdr_histogram* h)
562 {
563 struct hdr_iter iter;
564 int64_t total = 0;
565
566 hdr_iter_init(&iter, h);
567
568 while (hdr_iter_next(&iter))
569 {
570 if (0 != iter.count)
571 {
572 total += iter.count * hdr_median_equivalent_value(h, iter.value);
573 }
574 }
575
576 return (total * 1.0) / h->total_count;
577 }
578
hdr_stddev(const struct hdr_histogram * h)579 double hdr_stddev(const struct hdr_histogram* h)
580 {
581 double mean = hdr_mean(h);
582 double geometric_dev_total = 0.0;
583
584 struct hdr_iter iter;
585 hdr_iter_init(&iter, h);
586
587 while (hdr_iter_next(&iter))
588 {
589 if (0 != iter.count)
590 {
591 double dev = (hdr_median_equivalent_value(h, iter.value) * 1.0) - mean;
592 geometric_dev_total += (dev * dev) * iter.count;
593 }
594 }
595
596 return sqrt(geometric_dev_total / h->total_count);
597 }
598
hdr_values_are_equivalent(const struct hdr_histogram * h,int64_t a,int64_t b)599 bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b)
600 {
601 return lowest_equivalent_value(h, a) == lowest_equivalent_value(h, b);
602 }
603
hdr_lowest_equivalent_value(const struct hdr_histogram * h,int64_t value)604 int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value)
605 {
606 return lowest_equivalent_value(h, value);
607 }
608
hdr_count_at_value(const struct hdr_histogram * h,int64_t value)609 int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value)
610 {
611 return counts_get_normalised(h, counts_index_for(h, value));
612 }
613
hdr_count_at_index(const struct hdr_histogram * h,int32_t index)614 int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index)
615 {
616 return counts_get_normalised(h, index);
617 }
618
619
620 /* #### ######## ######## ######## ### ######## ####### ######## ###### */
621 /* ## ## ## ## ## ## ## ## ## ## ## ## ## ## */
622 /* ## ## ## ## ## ## ## ## ## ## ## ## ## */
623 /* ## ## ###### ######## ## ## ## ## ## ######## ###### */
624 /* ## ## ## ## ## ######### ## ## ## ## ## ## */
625 /* ## ## ## ## ## ## ## ## ## ## ## ## ## ## */
626 /* #### ## ######## ## ## ## ## ## ####### ## ## ###### */
627
628
has_buckets(struct hdr_iter * iter)629 static bool has_buckets(struct hdr_iter* iter)
630 {
631 return iter->counts_index < iter->h->counts_len;
632 }
633
has_next(struct hdr_iter * iter)634 static bool has_next(struct hdr_iter* iter)
635 {
636 return iter->cumulative_count < iter->total_count;
637 }
638
move_next(struct hdr_iter * iter)639 static bool move_next(struct hdr_iter* iter)
640 {
641 iter->counts_index++;
642
643 if (!has_buckets(iter))
644 {
645 return false;
646 }
647
648 iter->count = counts_get_normalised(iter->h, iter->counts_index);
649 iter->cumulative_count += iter->count;
650
651 iter->value = hdr_value_at_index(iter->h, iter->counts_index);
652 iter->highest_equivalent_value = highest_equivalent_value(iter->h, iter->value);
653 iter->lowest_equivalent_value = lowest_equivalent_value(iter->h, iter->value);
654 iter->median_equivalent_value = hdr_median_equivalent_value(iter->h, iter->value);
655
656 return true;
657 }
658
peek_next_value_from_index(struct hdr_iter * iter)659 static int64_t peek_next_value_from_index(struct hdr_iter* iter)
660 {
661 return hdr_value_at_index(iter->h, iter->counts_index + 1);
662 }
663
next_value_greater_than_reporting_level_upper_bound(struct hdr_iter * iter,int64_t reporting_level_upper_bound)664 static bool next_value_greater_than_reporting_level_upper_bound(
665 struct hdr_iter *iter, int64_t reporting_level_upper_bound)
666 {
667 if (iter->counts_index >= iter->h->counts_len)
668 {
669 return false;
670 }
671
672 return peek_next_value_from_index(iter) > reporting_level_upper_bound;
673 }
674
_basic_iter_next(struct hdr_iter * iter)675 static bool _basic_iter_next(struct hdr_iter *iter)
676 {
677 if (!has_next(iter) || iter->counts_index >= iter->h->counts_len)
678 {
679 return false;
680 }
681
682 move_next(iter);
683
684 return true;
685 }
686
_update_iterated_values(struct hdr_iter * iter,int64_t new_value_iterated_to)687 static void _update_iterated_values(struct hdr_iter* iter, int64_t new_value_iterated_to)
688 {
689 iter->value_iterated_from = iter->value_iterated_to;
690 iter->value_iterated_to = new_value_iterated_to;
691 }
692
_all_values_iter_next(struct hdr_iter * iter)693 static bool _all_values_iter_next(struct hdr_iter* iter)
694 {
695 bool result = move_next(iter);
696
697 if (result)
698 {
699 _update_iterated_values(iter, iter->value);
700 }
701
702 return result;
703 }
704
hdr_iter_init(struct hdr_iter * iter,const struct hdr_histogram * h)705 void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h)
706 {
707 iter->h = h;
708
709 iter->counts_index = -1;
710 iter->total_count = h->total_count;
711 iter->count = 0;
712 iter->cumulative_count = 0;
713 iter->value = 0;
714 iter->highest_equivalent_value = 0;
715 iter->value_iterated_from = 0;
716 iter->value_iterated_to = 0;
717
718 iter->_next_fp = _all_values_iter_next;
719 }
720
hdr_iter_next(struct hdr_iter * iter)721 bool hdr_iter_next(struct hdr_iter* iter)
722 {
723 return iter->_next_fp(iter);
724 }
725
726 /* ######## ######## ######## ###### ######## ## ## ######## #### ## ######## ###### */
727 /* ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## ## */
728 /* ## ## ## ## ## ## ## #### ## ## ## ## ## ## */
729 /* ######## ###### ######## ## ###### ## ## ## ## ## ## ###### ###### */
730 /* ## ## ## ## ## ## ## #### ## ## ## ## ## */
731 /* ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## */
732 /* ## ######## ## ## ###### ######## ## ## ## #### ######## ######## ###### */
733
_percentile_iter_next(struct hdr_iter * iter)734 static bool _percentile_iter_next(struct hdr_iter* iter)
735 {
736 int64_t temp, half_distance, percentile_reporting_ticks;
737
738 struct hdr_iter_percentiles* percentiles = &iter->specifics.percentiles;
739
740 if (!has_next(iter))
741 {
742 if (percentiles->seen_last_value)
743 {
744 return false;
745 }
746
747 percentiles->seen_last_value = true;
748 percentiles->percentile = 100.0;
749
750 return true;
751 }
752
753 if (iter->counts_index == -1 && !_basic_iter_next(iter))
754 {
755 return false;
756 }
757
758 do
759 {
760 double current_percentile = (100.0 * (double) iter->cumulative_count) / iter->h->total_count;
761 if (iter->count != 0 &&
762 percentiles->percentile_to_iterate_to <= current_percentile)
763 {
764 _update_iterated_values(iter, highest_equivalent_value(iter->h, iter->value));
765
766 percentiles->percentile = percentiles->percentile_to_iterate_to;
767 temp = (int64_t)(log(100 / (100.0 - (percentiles->percentile_to_iterate_to))) / log(2)) + 1;
768 half_distance = (int64_t) pow(2, (double) temp);
769 percentile_reporting_ticks = percentiles->ticks_per_half_distance * half_distance;
770 percentiles->percentile_to_iterate_to += 100.0 / percentile_reporting_ticks;
771
772 return true;
773 }
774 }
775 while (_basic_iter_next(iter));
776
777 return true;
778 }
779
hdr_iter_percentile_init(struct hdr_iter * iter,const struct hdr_histogram * h,int32_t ticks_per_half_distance)780 void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance)
781 {
782 iter->h = h;
783
784 hdr_iter_init(iter, h);
785
786 iter->specifics.percentiles.seen_last_value = false;
787 iter->specifics.percentiles.ticks_per_half_distance = ticks_per_half_distance;
788 iter->specifics.percentiles.percentile_to_iterate_to = 0.0;
789 iter->specifics.percentiles.percentile = 0.0;
790
791 iter->_next_fp = _percentile_iter_next;
792 }
793
format_line_string(char * str,size_t len,int significant_figures,format_type format)794 static void format_line_string(char* str, size_t len, int significant_figures, format_type format)
795 {
796 #if defined(_MSC_VER)
797 #define snprintf _snprintf
798 #pragma warning(push)
799 #pragma warning(disable: 4996)
800 #endif
801 const char* format_str = "%s%d%s";
802
803 switch (format)
804 {
805 case CSV:
806 snprintf(str, len, format_str, "%.", significant_figures, "f,%f,%d,%.2f\n");
807 break;
808 case CLASSIC:
809 snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n");
810 break;
811 default:
812 snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n");
813 }
814 #if defined(_MSC_VER)
815 #undef snprintf
816 #pragma warning(pop)
817 #endif
818 }
819
820
821 /* ######## ######## ###### ####### ######## ######## ######## ######## */
822 /* ## ## ## ## ## ## ## ## ## ## ## ## ## ## */
823 /* ## ## ## ## ## ## ## ## ## ## ## ## ## */
824 /* ######## ###### ## ## ## ######## ## ## ###### ## ## */
825 /* ## ## ## ## ## ## ## ## ## ## ## ## ## */
826 /* ## ## ## ## ## ## ## ## ## ## ## ## ## ## */
827 /* ## ## ######## ###### ####### ## ## ######## ######## ######## */
828
829
_recorded_iter_next(struct hdr_iter * iter)830 static bool _recorded_iter_next(struct hdr_iter* iter)
831 {
832 while (_basic_iter_next(iter))
833 {
834 if (iter->count != 0)
835 {
836 _update_iterated_values(iter, iter->value);
837
838 iter->specifics.recorded.count_added_in_this_iteration_step = iter->count;
839 return true;
840 }
841 }
842
843 return false;
844 }
845
hdr_iter_recorded_init(struct hdr_iter * iter,const struct hdr_histogram * h)846 void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h)
847 {
848 hdr_iter_init(iter, h);
849
850 iter->specifics.recorded.count_added_in_this_iteration_step = 0;
851
852 iter->_next_fp = _recorded_iter_next;
853 }
854
855 /* ## #### ## ## ######## ### ######## */
856 /* ## ## ### ## ## ## ## ## ## */
857 /* ## ## #### ## ## ## ## ## ## */
858 /* ## ## ## ## ## ###### ## ## ######## */
859 /* ## ## ## #### ## ######### ## ## */
860 /* ## ## ## ### ## ## ## ## ## */
861 /* ######## #### ## ## ######## ## ## ## ## */
862
863
_iter_linear_next(struct hdr_iter * iter)864 static bool _iter_linear_next(struct hdr_iter* iter)
865 {
866 struct hdr_iter_linear* linear = &iter->specifics.linear;
867
868 linear->count_added_in_this_iteration_step = 0;
869
870 if (has_next(iter) ||
871 next_value_greater_than_reporting_level_upper_bound(
872 iter, linear->next_value_reporting_level_lowest_equivalent))
873 {
874 do
875 {
876 if (iter->value >= linear->next_value_reporting_level_lowest_equivalent)
877 {
878 _update_iterated_values(iter, linear->next_value_reporting_level);
879
880 linear->next_value_reporting_level += linear->value_units_per_bucket;
881 linear->next_value_reporting_level_lowest_equivalent =
882 lowest_equivalent_value(iter->h, linear->next_value_reporting_level);
883
884 return true;
885 }
886
887 if (!move_next(iter))
888 {
889 return true;
890 }
891
892 linear->count_added_in_this_iteration_step += iter->count;
893 }
894 while (true);
895 }
896
897 return false;
898 }
899
900
hdr_iter_linear_init(struct hdr_iter * iter,const struct hdr_histogram * h,int64_t value_units_per_bucket)901 void hdr_iter_linear_init(struct hdr_iter* iter, const struct hdr_histogram* h, int64_t value_units_per_bucket)
902 {
903 hdr_iter_init(iter, h);
904
905 iter->specifics.linear.count_added_in_this_iteration_step = 0;
906 iter->specifics.linear.value_units_per_bucket = value_units_per_bucket;
907 iter->specifics.linear.next_value_reporting_level = value_units_per_bucket;
908 iter->specifics.linear.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_per_bucket);
909
910 iter->_next_fp = _iter_linear_next;
911 }
912
913 /* ## ####### ###### ### ######## #### ######## ## ## ## ## #### ###### */
914 /* ## ## ## ## ## ## ## ## ## ## ## ## ## ### ### ## ## ## */
915 /* ## ## ## ## ## ## ## ## ## ## ## ## #### #### ## ## */
916 /* ## ## ## ## #### ## ## ######## ## ## ######### ## ### ## ## ## */
917 /* ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ## */
918 /* ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## */
919 /* ######## ####### ###### ## ## ## ## #### ## ## ## ## ## #### ###### */
920
_log_iter_next(struct hdr_iter * iter)921 static bool _log_iter_next(struct hdr_iter *iter)
922 {
923 struct hdr_iter_log* logarithmic = &iter->specifics.log;
924
925 logarithmic->count_added_in_this_iteration_step = 0;
926
927 if (has_next(iter) ||
928 next_value_greater_than_reporting_level_upper_bound(
929 iter, logarithmic->next_value_reporting_level_lowest_equivalent))
930 {
931 do
932 {
933 if (iter->value >= logarithmic->next_value_reporting_level_lowest_equivalent)
934 {
935 _update_iterated_values(iter, logarithmic->next_value_reporting_level);
936
937 logarithmic->next_value_reporting_level *= (int64_t)logarithmic->log_base;
938 logarithmic->next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(iter->h, logarithmic->next_value_reporting_level);
939
940 return true;
941 }
942
943 if (!move_next(iter))
944 {
945 return true;
946 }
947
948 logarithmic->count_added_in_this_iteration_step += iter->count;
949 }
950 while (true);
951 }
952
953 return false;
954 }
955
hdr_iter_log_init(struct hdr_iter * iter,const struct hdr_histogram * h,int64_t value_units_first_bucket,double log_base)956 void hdr_iter_log_init(
957 struct hdr_iter* iter,
958 const struct hdr_histogram* h,
959 int64_t value_units_first_bucket,
960 double log_base)
961 {
962 hdr_iter_init(iter, h);
963 iter->specifics.log.count_added_in_this_iteration_step = 0;
964 iter->specifics.log.log_base = log_base;
965 iter->specifics.log.next_value_reporting_level = value_units_first_bucket;
966 iter->specifics.log.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_first_bucket);
967
968 iter->_next_fp = _log_iter_next;
969 }
970
971 /* Printing. */
972
format_head_string(format_type format)973 static const char* format_head_string(format_type format)
974 {
975 switch (format)
976 {
977 case CSV:
978 return "%s,%s,%s,%s\n";
979 case CLASSIC:
980 return "%12s %12s %12s %12s\n\n";
981 default:
982 return "%12s %12s %12s %12s\n\n";
983 }
984 }
985
986 static const char CLASSIC_FOOTER[] =
987 "#[Mean = %12.3f, StdDeviation = %12.3f]\n"
988 "#[Max = %12.3f, Total count = %12" PRIu64 "]\n"
989 "#[Buckets = %12d, SubBuckets = %12d]\n";
990
hdr_percentiles_print(struct hdr_histogram * h,FILE * stream,int32_t ticks_per_half_distance,double value_scale,format_type format)991 int hdr_percentiles_print(
992 struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance,
993 double value_scale, format_type format)
994 {
995 char line_format[25];
996 const char* head_format;
997 int rc = 0;
998 struct hdr_iter iter;
999 struct hdr_iter_percentiles * percentiles;
1000
1001 format_line_string(line_format, 25, h->significant_figures, format);
1002 head_format = format_head_string(format);
1003
1004 hdr_iter_percentile_init(&iter, h, ticks_per_half_distance);
1005
1006 if (fprintf(
1007 stream, head_format,
1008 "Value", "Percentile", "TotalCount", "1/(1-Percentile)") < 0)
1009 {
1010 rc = EIO;
1011 goto cleanup;
1012 }
1013
1014 percentiles = &iter.specifics.percentiles;
1015 while (hdr_iter_next(&iter))
1016 {
1017 double value = iter.highest_equivalent_value / value_scale;
1018 double percentile = percentiles->percentile / 100.0;
1019 int64_t total_count = iter.cumulative_count;
1020 double inverted_percentile = (1.0 / (1.0 - percentile));
1021
1022 if (fprintf(
1023 stream, line_format, value, percentile, total_count, inverted_percentile) < 0)
1024 {
1025 rc = EIO;
1026 goto cleanup;
1027 }
1028 }
1029
1030 if (CLASSIC == format)
1031 {
1032 double mean = hdr_mean(h) / value_scale;
1033 double stddev = hdr_stddev(h) / value_scale;
1034 double max = hdr_max(h) / value_scale;
1035
1036 if (fprintf(
1037 stream, CLASSIC_FOOTER, mean, stddev, max,
1038 h->total_count, h->bucket_count, h->sub_bucket_count) < 0)
1039 {
1040 rc = EIO;
1041 goto cleanup;
1042 }
1043 }
1044
1045 cleanup:
1046 return rc;
1047 }
1048