diff options
author | Sergey Polovko <sergey@polovko.me> | 2022-02-10 16:47:02 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:47:02 +0300 |
commit | 3e0b762a82514bac89c1dd6ea7211e381d8aa248 (patch) | |
tree | c2d1b379ecaf05ca8f11ed0b5da9d1a950e6e554 /contrib/libs/hdr_histogram/src/hdr_histogram.c | |
parent | ab3783171cc30e262243a0227c86118f7080c896 (diff) | |
download | ydb-3e0b762a82514bac89c1dd6ea7211e381d8aa248.tar.gz |
Restoring authorship annotation for Sergey Polovko <sergey@polovko.me>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/hdr_histogram/src/hdr_histogram.c')
-rw-r--r-- | contrib/libs/hdr_histogram/src/hdr_histogram.c | 2020 |
1 files changed, 1010 insertions, 1010 deletions
diff --git a/contrib/libs/hdr_histogram/src/hdr_histogram.c b/contrib/libs/hdr_histogram/src/hdr_histogram.c index 8cad5a0d61..044afe2a22 100644 --- a/contrib/libs/hdr_histogram/src/hdr_histogram.c +++ b/contrib/libs/hdr_histogram/src/hdr_histogram.c @@ -1,1010 +1,1010 @@ -/** - * hdr_histogram.c - * Written by Michael Barker and released to the public domain, - * as explained at http://creativecommons.org/publicdomain/zero/1.0/ - */ - -#include <stdlib.h> -#include <stdbool.h> -#include <math.h> -#include <assert.h> -#include <stdio.h> -#include <string.h> -#include <stdint.h> -#include <errno.h> -#include <inttypes.h> - -#include "hdr_histogram.h" -#include "hdr_tests.h" - -// ###### ####### ## ## ## ## ######## ###### -// ## ## ## ## ## ## ### ## ## ## ## -// ## ## ## ## ## #### ## ## ## -// ## ## ## ## ## ## ## ## ## ###### -// ## ## ## ## ## ## #### ## ## -// ## ## ## ## ## ## ## ### ## ## ## -// ###### ####### ####### ## ## ## ###### - -static int32_t normalize_index(const struct hdr_histogram* h, int32_t index) -{ - if (h->normalizing_index_offset == 0) - { - return index; - } - - int32_t normalized_index = index - h->normalizing_index_offset; - int32_t adjustment = 0; - - if (normalized_index < 0) - { - adjustment = h->counts_len; - } - else if (normalized_index >= h->counts_len) - { - adjustment = -h->counts_len; - } - - return normalized_index + adjustment; -} - -static int64_t counts_get_direct(const struct hdr_histogram* h, int32_t index) -{ - return h->counts[index]; -} - -static int64_t counts_get_normalised(const struct hdr_histogram* h, int32_t index) -{ - return counts_get_direct(h, normalize_index(h, index)); -} - -static void counts_inc_normalised( - struct hdr_histogram* h, int32_t index, int64_t value) -{ - int32_t normalised_index = normalize_index(h, index); - h->counts[normalised_index] += value; - h->total_count += value; -} - -static void update_min_max(struct hdr_histogram* h, int64_t value) -{ - h->min_value = (value < h->min_value && value != 0) ? value : h->min_value; - h->max_value = (value > h->max_value) ? value : h->max_value; -} - -// ## ## ######## #### ## #### ######## ## ## -// ## ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## #### -// ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## -// ####### ## #### ######## #### ## ## - -static int64_t power(int64_t base, int64_t exp) -{ - int64_t result = 1; - while(exp) - { - result *= base; exp--; - } - return result; -} - -#if defined(_MSC_VER) -#pragma intrinsic(_BitScanReverse64) -#endif - -static int32_t get_bucket_index(const struct hdr_histogram* h, int64_t value) -{ -#if defined(_MSC_VER) - uint32_t leading_zero = 0; - _BitScanReverse64(&leading_zero, value | h->sub_bucket_mask); - int32_t pow2ceiling = 64 - (63 - leading_zero); // smallest power of 2 containing value -#else - int32_t pow2ceiling = 64 - __builtin_clzll(value | h->sub_bucket_mask); // smallest power of 2 containing value -#endif - return pow2ceiling - h->unit_magnitude - (h->sub_bucket_half_count_magnitude + 1); -} - -static int32_t get_sub_bucket_index(int64_t value, int32_t bucket_index, int32_t unit_magnitude) -{ - return (int32_t)(value >> (bucket_index + unit_magnitude)); -} - -static int32_t counts_index(const struct hdr_histogram* h, int32_t bucket_index, int32_t sub_bucket_index) -{ - // Calculate the index for the first entry in the bucket: - // (The following is the equivalent of ((bucket_index + 1) * subBucketHalfCount) ): - int32_t bucket_base_index = (bucket_index + 1) << h->sub_bucket_half_count_magnitude; - // Calculate the offset in the bucket: - int32_t offset_in_bucket = sub_bucket_index - h->sub_bucket_half_count; - // The following is the equivalent of ((sub_bucket_index - subBucketHalfCount) + bucketBaseIndex; - return bucket_base_index + offset_in_bucket; -} - -static int64_t value_from_index(int32_t bucket_index, int32_t sub_bucket_index, int32_t unit_magnitude) -{ - return ((int64_t) sub_bucket_index) << (bucket_index + unit_magnitude); -} - -int32_t counts_index_for(const struct hdr_histogram* h, int64_t value) -{ - int32_t bucket_index = get_bucket_index(h, value); - int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude); - - return counts_index(h, bucket_index, sub_bucket_index); -} - -int64_t hdr_value_at_index(const struct hdr_histogram *h, int32_t index) -{ - int32_t bucket_index = (index >> h->sub_bucket_half_count_magnitude) - 1; - int32_t sub_bucket_index = (index & (h->sub_bucket_half_count - 1)) + h->sub_bucket_half_count; - - if (bucket_index < 0) - { - sub_bucket_index -= h->sub_bucket_half_count; - bucket_index = 0; - } - - return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude); -} - -int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value) -{ - int32_t bucket_index = get_bucket_index(h, value); - int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude); - int32_t adjusted_bucket = (sub_bucket_index >= h->sub_bucket_count) ? (bucket_index + 1) : bucket_index; - return INT64_C(1) << (h->unit_magnitude + adjusted_bucket); -} - -static int64_t lowest_equivalent_value(const struct hdr_histogram* h, int64_t value) -{ - int32_t bucket_index = get_bucket_index(h, value); - int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude); - return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude); -} - -int64_t hdr_next_non_equivalent_value(const struct hdr_histogram *h, int64_t value) -{ - return lowest_equivalent_value(h, value) + hdr_size_of_equivalent_value_range(h, value); -} - -static int64_t highest_equivalent_value(const struct hdr_histogram* h, int64_t value) -{ - return hdr_next_non_equivalent_value(h, value) - 1; -} - -int64_t hdr_median_equivalent_value(const struct hdr_histogram *h, int64_t value) -{ - return lowest_equivalent_value(h, value) + (hdr_size_of_equivalent_value_range(h, value) >> 1); -} - -static int64_t non_zero_min(const struct hdr_histogram* h) -{ - if (INT64_MAX == h->min_value) - { - return INT64_MAX; - } - - return lowest_equivalent_value(h, h->min_value); -} - -void hdr_reset_internal_counters(struct hdr_histogram* h) -{ - int min_non_zero_index = -1; - int max_index = -1; - int64_t observed_total_count = 0; - int i; - - for (i = 0; i < h->counts_len; i++) - { - int64_t count_at_index; - - if ((count_at_index = counts_get_direct(h, i)) > 0) - { - observed_total_count += count_at_index; - max_index = i; - if (min_non_zero_index == -1 && i != 0) - { - min_non_zero_index = i; - } - } - } - - if (max_index == -1) - { - h->max_value = 0; - } - else - { - int64_t max_value = hdr_value_at_index(h, max_index); - h->max_value = highest_equivalent_value(h, max_value); - } - - if (min_non_zero_index == -1) - { - h->min_value = INT64_MAX; - } - else - { - h->min_value = hdr_value_at_index(h, min_non_zero_index); - } - - h->total_count = observed_total_count; -} - -static int32_t buckets_needed_to_cover_value(int64_t value, int32_t sub_bucket_count, int32_t unit_magnitude) -{ - int64_t smallest_untrackable_value = ((int64_t) sub_bucket_count) << unit_magnitude; - int32_t buckets_needed = 1; - while (smallest_untrackable_value <= value) - { - if (smallest_untrackable_value > INT64_MAX / 2) - { - return buckets_needed + 1; - } - smallest_untrackable_value <<= 1; - buckets_needed++; - } - - return buckets_needed; -} - -// ## ## ######## ## ## ####### ######## ## ## -// ### ### ## ### ### ## ## ## ## ## ## -// #### #### ## #### #### ## ## ## ## #### -// ## ### ## ###### ## ### ## ## ## ######## ## -// ## ## ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## ## ## -// ## ## ######## ## ## ####### ## ## ## - -int hdr_calculate_bucket_config( - int64_t lowest_trackable_value, - int64_t highest_trackable_value, - int significant_figures, - struct hdr_histogram_bucket_config* cfg) -{ - if (lowest_trackable_value < 1 || - significant_figures < 1 || 5 < significant_figures) - { - return EINVAL; - } - else if (lowest_trackable_value * 2 > highest_trackable_value) - { - return EINVAL; - } - - cfg->lowest_trackable_value = lowest_trackable_value; - cfg->significant_figures = significant_figures; - cfg->highest_trackable_value = highest_trackable_value; - - int64_t largest_value_with_single_unit_resolution = 2 * power(10, significant_figures); - int32_t sub_bucket_count_magnitude = (int32_t) ceil(log((double)largest_value_with_single_unit_resolution) / log(2)); - cfg->sub_bucket_half_count_magnitude = ((sub_bucket_count_magnitude > 1) ? sub_bucket_count_magnitude : 1) - 1; - - cfg->unit_magnitude = (int32_t) floor(log((double)lowest_trackable_value) / log(2)); - - cfg->sub_bucket_count = (int32_t) pow(2, (cfg->sub_bucket_half_count_magnitude + 1)); - cfg->sub_bucket_half_count = cfg->sub_bucket_count / 2; - cfg->sub_bucket_mask = ((int64_t) cfg->sub_bucket_count - 1) << cfg->unit_magnitude; - - // determine exponent range needed to support the trackable value with no overflow: - cfg->bucket_count = buckets_needed_to_cover_value(highest_trackable_value, cfg->sub_bucket_count, (int32_t)cfg->unit_magnitude); - cfg->counts_len = (cfg->bucket_count + 1) * (cfg->sub_bucket_count / 2); - - return 0; -} - -void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg) -{ - h->lowest_trackable_value = cfg->lowest_trackable_value; - h->highest_trackable_value = cfg->highest_trackable_value; - h->unit_magnitude = (int32_t)cfg->unit_magnitude; - h->significant_figures = (int32_t)cfg->significant_figures; - h->sub_bucket_half_count_magnitude = cfg->sub_bucket_half_count_magnitude; - h->sub_bucket_half_count = cfg->sub_bucket_half_count; - h->sub_bucket_mask = cfg->sub_bucket_mask; - h->sub_bucket_count = cfg->sub_bucket_count; - h->min_value = INT64_MAX; - h->max_value = 0; - h->normalizing_index_offset = 0; - h->conversion_ratio = 1.0; - h->bucket_count = cfg->bucket_count; - h->counts_len = cfg->counts_len; - h->total_count = 0; -} - -int hdr_init( - int64_t lowest_trackable_value, - int64_t highest_trackable_value, - int significant_figures, - struct hdr_histogram** result) -{ - struct hdr_histogram_bucket_config cfg; - - int r = hdr_calculate_bucket_config(lowest_trackable_value, highest_trackable_value, significant_figures, &cfg); - if (r) - { - return r; - } - - size_t histogram_size = sizeof(struct hdr_histogram) + cfg.counts_len * sizeof(int64_t); - struct hdr_histogram* histogram = malloc(histogram_size); - - if (!histogram) - { - return ENOMEM; - } - - // memset will ensure that all of the function pointers are null. - memset((void*) histogram, 0, histogram_size); - - hdr_init_preallocated(histogram, &cfg); - *result = histogram; - - return 0; -} - - -int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result) -{ - return hdr_init(1, highest_trackable_value, significant_figures, result); -} - -// reset a histogram to zero. -void hdr_reset(struct hdr_histogram *h) -{ - h->total_count=0; - h->min_value = INT64_MAX; - h->max_value = 0; - memset((void *) &h->counts, 0, (sizeof(int64_t) * h->counts_len)); - return; -} - -size_t hdr_get_memory_size(struct hdr_histogram *h) -{ - return sizeof(struct hdr_histogram) + h->counts_len * sizeof(int64_t); -} - -// ## ## ######## ######## ### ######## ######## ###### -// ## ## ## ## ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## ## ## ## -// ## ## ######## ## ## ## ## ## ###### ###### -// ## ## ## ## ## ######### ## ## ## -// ## ## ## ## ## ## ## ## ## ## ## -// ####### ## ######## ## ## ## ######## ###### - - -bool hdr_record_value(struct hdr_histogram* h, int64_t value) -{ - return hdr_record_values(h, value, 1); -} - -bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count) -{ - if (value < 0) - { - return false; - } - - int32_t counts_index = counts_index_for(h, value); - - if (counts_index < 0 || h->counts_len <= counts_index) - { - return false; - } - - counts_inc_normalised(h, counts_index, count); - update_min_max(h, value); - - return true; -} - -bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expected_interval) -{ - return hdr_record_corrected_values(h, value, 1, expected_interval); -} - - -bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval) -{ - if (!hdr_record_values(h, value, count)) - { - return false; - } - - if (expected_interval <= 0 || value <= expected_interval) - { - return true; - } - - int64_t missing_value = value - expected_interval; - for (; missing_value >= expected_interval; missing_value -= expected_interval) - { - if (!hdr_record_values(h, missing_value, count)) - { - return false; - } - } - - return true; -} - -int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from) -{ - struct hdr_iter iter; - hdr_iter_recorded_init(&iter, from); - int64_t dropped = 0; - - while (hdr_iter_next(&iter)) - { - int64_t value = iter.value; - int64_t count = iter.count; - - if (!hdr_record_values(h, value, count)) - { - dropped += count; - } - } - - return dropped; -} - -int64_t hdr_add_while_correcting_for_coordinated_omission( - struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval) -{ - struct hdr_iter iter; - hdr_iter_recorded_init(&iter, from); - int64_t dropped = 0; - - while (hdr_iter_next(&iter)) - { - int64_t value = iter.value; - int64_t count = iter.count; - - if (!hdr_record_corrected_values(h, value, count, expected_interval)) - { - dropped += count; - } - } - - return dropped; -} - - - -// ## ## ### ## ## ## ######## ###### -// ## ## ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ###### ###### -// ## ## ######### ## ## ## ## ## -// ## ## ## ## ## ## ## ## ## ## -// ### ## ## ######## ####### ######## ###### - - -int64_t hdr_max(const struct hdr_histogram* h) -{ - if (0 == h->max_value) - { - return 0; - } - - return highest_equivalent_value(h, h->max_value); -} - -int64_t hdr_min(const struct hdr_histogram* h) -{ - if (0 < hdr_count_at_index(h, 0)) - { - return 0; - } - - return non_zero_min(h); -} - -int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile) -{ - struct hdr_iter iter; - hdr_iter_init(&iter, h); - - double requested_percentile = percentile < 100.0 ? percentile : 100.0; - int64_t count_at_percentile = - (int64_t) (((requested_percentile / 100) * h->total_count) + 0.5); - count_at_percentile = count_at_percentile > 1 ? count_at_percentile : 1; - int64_t total = 0; - - while (hdr_iter_next(&iter)) - { - total += iter.count; - - if (total >= count_at_percentile) - { - int64_t value_from_index = iter.value; - return highest_equivalent_value(h, value_from_index); - } - } - - return 0; -} - -double hdr_mean(const struct hdr_histogram* h) -{ - struct hdr_iter iter; - int64_t total = 0; - - hdr_iter_init(&iter, h); - - while (hdr_iter_next(&iter)) - { - if (0 != iter.count) - { - total += iter.count * hdr_median_equivalent_value(h, iter.value); - } - } - - return (total * 1.0) / h->total_count; -} - -double hdr_stddev(const struct hdr_histogram* h) -{ - double mean = hdr_mean(h); - double geometric_dev_total = 0.0; - - struct hdr_iter iter; - hdr_iter_init(&iter, h); - - while (hdr_iter_next(&iter)) - { - if (0 != iter.count) - { - double dev = (hdr_median_equivalent_value(h, iter.value) * 1.0) - mean; - geometric_dev_total += (dev * dev) * iter.count; - } - } - - return sqrt(geometric_dev_total / h->total_count); -} - -bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b) -{ - return lowest_equivalent_value(h, a) == lowest_equivalent_value(h, b); -} - -int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value) -{ - return lowest_equivalent_value(h, value); -} - -int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value) -{ - return counts_get_normalised(h, counts_index_for(h, value)); -} - -int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index) -{ - return counts_get_normalised(h, index); -} - - -// #### ######## ######## ######## ### ######## ####### ######## ###### -// ## ## ## ## ## ## ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## ## ## ## ## ## -// ## ## ###### ######## ## ## ## ## ## ######## ###### -// ## ## ## ## ## ######### ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## ## ## ## ## ## ## -// #### ## ######## ## ## ## ## ## ####### ## ## ###### - - -static bool has_buckets(struct hdr_iter* iter) -{ - return iter->counts_index < iter->h->counts_len; -} - -static bool has_next(struct hdr_iter* iter) -{ - return iter->cumulative_count < iter->total_count; -} - -static bool move_next(struct hdr_iter* iter) -{ - iter->counts_index++; - - if (!has_buckets(iter)) - { - return false; - } - - iter->count = counts_get_normalised(iter->h, iter->counts_index); - iter->cumulative_count += iter->count; - - iter->value = hdr_value_at_index(iter->h, iter->counts_index); - iter->highest_equivalent_value = highest_equivalent_value(iter->h, iter->value); - iter->lowest_equivalent_value = lowest_equivalent_value(iter->h, iter->value); - iter->median_equivalent_value = hdr_median_equivalent_value(iter->h, iter->value); - - return true; -} - -static int64_t peek_next_value_from_index(struct hdr_iter* iter) -{ - return hdr_value_at_index(iter->h, iter->counts_index + 1); -} - -static bool next_value_greater_than_reporting_level_upper_bound( - struct hdr_iter *iter, int64_t reporting_level_upper_bound) -{ - if (iter->counts_index >= iter->h->counts_len) - { - return false; - } - - return peek_next_value_from_index(iter) > reporting_level_upper_bound; -} - -static bool _basic_iter_next(struct hdr_iter *iter) -{ - if (!has_next(iter)) - { - return false; - } - - move_next(iter); - - return true; -} - -static void _update_iterated_values(struct hdr_iter* iter, int64_t new_value_iterated_to) -{ - iter->value_iterated_from = iter->value_iterated_to; - iter->value_iterated_to = new_value_iterated_to; -} - -static bool _all_values_iter_next(struct hdr_iter* iter) -{ - bool result = move_next(iter); - - if (result) - { - _update_iterated_values(iter, iter->value); - } - - return result; -} - -void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h) -{ - iter->h = h; - - iter->counts_index = -1; - iter->total_count = h->total_count; - iter->count = 0; - iter->cumulative_count = 0; - iter->value = 0; - iter->highest_equivalent_value = 0; - iter->value_iterated_from = 0; - iter->value_iterated_to = 0; - - iter->_next_fp = _all_values_iter_next; -} - -bool hdr_iter_next(struct hdr_iter* iter) -{ - return iter->_next_fp(iter); -} - -// ######## ######## ######## ###### ######## ## ## ######## #### ## ######## ###### -// ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## #### ## ## ## ## ## ## -// ######## ###### ######## ## ###### ## ## ## ## ## ## ###### ###### -// ## ## ## ## ## ## ## #### ## ## ## ## ## -// ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## -// ## ######## ## ## ###### ######## ## ## ## #### ######## ######## ###### - -static bool _percentile_iter_next(struct hdr_iter* iter) -{ - struct hdr_iter_percentiles* percentiles = &iter->specifics.percentiles; - - if (!has_next(iter)) - { - if (percentiles->seen_last_value) - { - return false; - } - - percentiles->seen_last_value = true; - percentiles->percentile = 100.0; - - return true; - } - - if (iter->counts_index == -1 && !_basic_iter_next(iter)) - { - return false; - } - - do - { - double current_percentile = (100.0 * (double) iter->cumulative_count) / iter->h->total_count; - if (iter->count != 0 && - percentiles->percentile_to_iterate_to <= current_percentile) - { - _update_iterated_values(iter, highest_equivalent_value(iter->h, iter->value)); - - percentiles->percentile = percentiles->percentile_to_iterate_to; - int64_t temp = (int64_t)(log(100 / (100.0 - (percentiles->percentile_to_iterate_to))) / log(2)) + 1; - int64_t half_distance = (int64_t) pow(2, (double) temp); - int64_t percentile_reporting_ticks = percentiles->ticks_per_half_distance * half_distance; - percentiles->percentile_to_iterate_to += 100.0 / percentile_reporting_ticks; - - return true; - } - } - while (_basic_iter_next(iter)); - - return true; -} - -void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance) -{ - iter->h = h; - - hdr_iter_init(iter, h); - - iter->specifics.percentiles.seen_last_value = false; - iter->specifics.percentiles.ticks_per_half_distance = ticks_per_half_distance; - iter->specifics.percentiles.percentile_to_iterate_to = 0.0; - iter->specifics.percentiles.percentile = 0.0; - - iter->_next_fp = _percentile_iter_next; -} - -static void format_line_string(char* str, size_t len, int significant_figures, format_type format) -{ -#if defined(_MSC_VER) -#define snprintf _snprintf -#pragma warning(push) -#pragma warning(disable: 4996) -#endif - const char* format_str = "%s%d%s"; - - switch (format) - { - case CSV: - snprintf(str, len, format_str, "%.", significant_figures, "f,%f,%d,%.2f\n"); - break; - case CLASSIC: - snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n"); - break; - default: - snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n"); - } -#if defined(_MSC_VER) -#undef snprintf -#pragma warning(pop) -#endif -} - - -// ######## ######## ###### ####### ######## ######## ######## ######## -// ## ## ## ## ## ## ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## ## ## ## ## ## -// ######## ###### ## ## ## ######## ## ## ###### ## ## -// ## ## ## ## ## ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## ## ## ## ## ## ## -// ## ## ######## ###### ####### ## ## ######## ######## ######## - - -static bool _recorded_iter_next(struct hdr_iter* iter) -{ - while (_basic_iter_next(iter)) - { - if (iter->count != 0) - { - _update_iterated_values(iter, iter->value); - - iter->specifics.recorded.count_added_in_this_iteration_step = iter->count; - return true; - } - } - - return false; -} - -void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h) -{ - hdr_iter_init(iter, h); - - iter->specifics.recorded.count_added_in_this_iteration_step = 0; - - iter->_next_fp = _recorded_iter_next; -} - -// ## #### ## ## ######## ### ######## -// ## ## ### ## ## ## ## ## ## -// ## ## #### ## ## ## ## ## ## -// ## ## ## ## ## ###### ## ## ######## -// ## ## ## #### ## ######### ## ## -// ## ## ## ### ## ## ## ## ## -// ######## #### ## ## ######## ## ## ## ## - - -static bool _iter_linear_next(struct hdr_iter* iter) -{ - struct hdr_iter_linear* linear = &iter->specifics.linear; - - linear->count_added_in_this_iteration_step = 0; - - if (has_next(iter) || - next_value_greater_than_reporting_level_upper_bound( - iter, linear->next_value_reporting_level_lowest_equivalent)) - { - do - { - if (iter->value >= linear->next_value_reporting_level_lowest_equivalent) - { - _update_iterated_values(iter, linear->next_value_reporting_level); - - linear->next_value_reporting_level += linear->value_units_per_bucket; - linear->next_value_reporting_level_lowest_equivalent = - lowest_equivalent_value(iter->h, linear->next_value_reporting_level); - - return true; - } - - if (!move_next(iter)) - { - return true; - } - - linear->count_added_in_this_iteration_step += iter->count; - } - while (true); - } - - return false; -} - - -void hdr_iter_linear_init(struct hdr_iter* iter, const struct hdr_histogram* h, int64_t value_units_per_bucket) -{ - hdr_iter_init(iter, h); - - iter->specifics.linear.count_added_in_this_iteration_step = 0; - iter->specifics.linear.value_units_per_bucket = value_units_per_bucket; - iter->specifics.linear.next_value_reporting_level = value_units_per_bucket; - iter->specifics.linear.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_per_bucket); - - iter->_next_fp = _iter_linear_next; -} - -// ## ####### ###### ### ######## #### ######## ## ## ## ## #### ###### -// ## ## ## ## ## ## ## ## ## ## ## ## ## ### ### ## ## ## -// ## ## ## ## ## ## ## ## ## ## ## ## #### #### ## ## -// ## ## ## ## #### ## ## ######## ## ## ######### ## ### ## ## ## -// ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ## -// ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## -// ######## ####### ###### ## ## ## ## #### ## ## ## ## ## #### ###### - -static bool _log_iter_next(struct hdr_iter *iter) -{ - struct hdr_iter_log* logarithmic = &iter->specifics.log; - - logarithmic->count_added_in_this_iteration_step = 0; - - if (has_next(iter) || - next_value_greater_than_reporting_level_upper_bound( - iter, logarithmic->next_value_reporting_level_lowest_equivalent)) - { - do - { - if (iter->value >= logarithmic->next_value_reporting_level_lowest_equivalent) - { - _update_iterated_values(iter, logarithmic->next_value_reporting_level); - - logarithmic->next_value_reporting_level *= (int64_t)logarithmic->log_base; - logarithmic->next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(iter->h, logarithmic->next_value_reporting_level); - - return true; - } - - if (!move_next(iter)) - { - return true; - } - - logarithmic->count_added_in_this_iteration_step += iter->count; - } - while (true); - } - - return false; -} - -void hdr_iter_log_init( - struct hdr_iter* iter, - const struct hdr_histogram* h, - int64_t value_units_first_bucket, - double log_base) -{ - hdr_iter_init(iter, h); - iter->specifics.log.count_added_in_this_iteration_step = 0; - iter->specifics.log.log_base = log_base; - iter->specifics.log.next_value_reporting_level = value_units_first_bucket; - iter->specifics.log.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_first_bucket); - - iter->_next_fp = _log_iter_next; -} - -// Printing. - -static const char* format_head_string(format_type format) -{ - switch (format) - { - case CSV: - return "%s,%s,%s,%s\n"; - case CLASSIC: - return "%12s %12s %12s %12s\n\n"; - default: - return "%12s %12s %12s %12s\n\n"; - } -} - -static const char CLASSIC_FOOTER[] = - "#[Mean = %12.3f, StdDeviation = %12.3f]\n" - "#[Max = %12.3f, Total count = %12" PRIu64 "]\n" - "#[Buckets = %12d, SubBuckets = %12d]\n"; - -int hdr_percentiles_print( - struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance, - double value_scale, format_type format) -{ - char line_format[25]; - format_line_string(line_format, 25, h->significant_figures, format); - const char* head_format = format_head_string(format); - int rc = 0; - - struct hdr_iter iter; - hdr_iter_percentile_init(&iter, h, ticks_per_half_distance); - - if (fprintf( - stream, head_format, - "Value", "Percentile", "TotalCount", "1/(1-Percentile)") < 0) - { - rc = EIO; - goto cleanup; - } - - struct hdr_iter_percentiles * percentiles = &iter.specifics.percentiles; - while (hdr_iter_next(&iter)) - { - double value = iter.highest_equivalent_value / value_scale; - double percentile = percentiles->percentile / 100.0; - int64_t total_count = iter.cumulative_count; - double inverted_percentile = (1.0 / (1.0 - percentile)); - - if (fprintf( - stream, line_format, value, percentile, total_count, inverted_percentile) < 0) - { - rc = EIO; - goto cleanup; - } - } - - if (CLASSIC == format) - { - double mean = hdr_mean(h) / value_scale; - double stddev = hdr_stddev(h) / value_scale; - double max = hdr_max(h) / value_scale; - - if (fprintf( - stream, CLASSIC_FOOTER, mean, stddev, max, - h->total_count, h->bucket_count, h->sub_bucket_count) < 0) - { - rc = EIO; - goto cleanup; - } - } - - cleanup: - return rc; -} +/** + * hdr_histogram.c + * Written by Michael Barker and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include <stdlib.h> +#include <stdbool.h> +#include <math.h> +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <inttypes.h> + +#include "hdr_histogram.h" +#include "hdr_tests.h" + +// ###### ####### ## ## ## ## ######## ###### +// ## ## ## ## ## ## ### ## ## ## ## +// ## ## ## ## ## #### ## ## ## +// ## ## ## ## ## ## ## ## ## ###### +// ## ## ## ## ## ## #### ## ## +// ## ## ## ## ## ## ## ### ## ## ## +// ###### ####### ####### ## ## ## ###### + +static int32_t normalize_index(const struct hdr_histogram* h, int32_t index) +{ + if (h->normalizing_index_offset == 0) + { + return index; + } + + int32_t normalized_index = index - h->normalizing_index_offset; + int32_t adjustment = 0; + + if (normalized_index < 0) + { + adjustment = h->counts_len; + } + else if (normalized_index >= h->counts_len) + { + adjustment = -h->counts_len; + } + + return normalized_index + adjustment; +} + +static int64_t counts_get_direct(const struct hdr_histogram* h, int32_t index) +{ + return h->counts[index]; +} + +static int64_t counts_get_normalised(const struct hdr_histogram* h, int32_t index) +{ + return counts_get_direct(h, normalize_index(h, index)); +} + +static void counts_inc_normalised( + struct hdr_histogram* h, int32_t index, int64_t value) +{ + int32_t normalised_index = normalize_index(h, index); + h->counts[normalised_index] += value; + h->total_count += value; +} + +static void update_min_max(struct hdr_histogram* h, int64_t value) +{ + h->min_value = (value < h->min_value && value != 0) ? value : h->min_value; + h->max_value = (value > h->max_value) ? value : h->max_value; +} + +// ## ## ######## #### ## #### ######## ## ## +// ## ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## #### +// ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## +// ####### ## #### ######## #### ## ## + +static int64_t power(int64_t base, int64_t exp) +{ + int64_t result = 1; + while(exp) + { + result *= base; exp--; + } + return result; +} + +#if defined(_MSC_VER) +#pragma intrinsic(_BitScanReverse64) +#endif + +static int32_t get_bucket_index(const struct hdr_histogram* h, int64_t value) +{ +#if defined(_MSC_VER) + uint32_t leading_zero = 0; + _BitScanReverse64(&leading_zero, value | h->sub_bucket_mask); + int32_t pow2ceiling = 64 - (63 - leading_zero); // smallest power of 2 containing value +#else + int32_t pow2ceiling = 64 - __builtin_clzll(value | h->sub_bucket_mask); // smallest power of 2 containing value +#endif + return pow2ceiling - h->unit_magnitude - (h->sub_bucket_half_count_magnitude + 1); +} + +static int32_t get_sub_bucket_index(int64_t value, int32_t bucket_index, int32_t unit_magnitude) +{ + return (int32_t)(value >> (bucket_index + unit_magnitude)); +} + +static int32_t counts_index(const struct hdr_histogram* h, int32_t bucket_index, int32_t sub_bucket_index) +{ + // Calculate the index for the first entry in the bucket: + // (The following is the equivalent of ((bucket_index + 1) * subBucketHalfCount) ): + int32_t bucket_base_index = (bucket_index + 1) << h->sub_bucket_half_count_magnitude; + // Calculate the offset in the bucket: + int32_t offset_in_bucket = sub_bucket_index - h->sub_bucket_half_count; + // The following is the equivalent of ((sub_bucket_index - subBucketHalfCount) + bucketBaseIndex; + return bucket_base_index + offset_in_bucket; +} + +static int64_t value_from_index(int32_t bucket_index, int32_t sub_bucket_index, int32_t unit_magnitude) +{ + return ((int64_t) sub_bucket_index) << (bucket_index + unit_magnitude); +} + +int32_t counts_index_for(const struct hdr_histogram* h, int64_t value) +{ + int32_t bucket_index = get_bucket_index(h, value); + int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude); + + return counts_index(h, bucket_index, sub_bucket_index); +} + +int64_t hdr_value_at_index(const struct hdr_histogram *h, int32_t index) +{ + int32_t bucket_index = (index >> h->sub_bucket_half_count_magnitude) - 1; + int32_t sub_bucket_index = (index & (h->sub_bucket_half_count - 1)) + h->sub_bucket_half_count; + + if (bucket_index < 0) + { + sub_bucket_index -= h->sub_bucket_half_count; + bucket_index = 0; + } + + return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude); +} + +int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value) +{ + int32_t bucket_index = get_bucket_index(h, value); + int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude); + int32_t adjusted_bucket = (sub_bucket_index >= h->sub_bucket_count) ? (bucket_index + 1) : bucket_index; + return INT64_C(1) << (h->unit_magnitude + adjusted_bucket); +} + +static int64_t lowest_equivalent_value(const struct hdr_histogram* h, int64_t value) +{ + int32_t bucket_index = get_bucket_index(h, value); + int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude); + return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude); +} + +int64_t hdr_next_non_equivalent_value(const struct hdr_histogram *h, int64_t value) +{ + return lowest_equivalent_value(h, value) + hdr_size_of_equivalent_value_range(h, value); +} + +static int64_t highest_equivalent_value(const struct hdr_histogram* h, int64_t value) +{ + return hdr_next_non_equivalent_value(h, value) - 1; +} + +int64_t hdr_median_equivalent_value(const struct hdr_histogram *h, int64_t value) +{ + return lowest_equivalent_value(h, value) + (hdr_size_of_equivalent_value_range(h, value) >> 1); +} + +static int64_t non_zero_min(const struct hdr_histogram* h) +{ + if (INT64_MAX == h->min_value) + { + return INT64_MAX; + } + + return lowest_equivalent_value(h, h->min_value); +} + +void hdr_reset_internal_counters(struct hdr_histogram* h) +{ + int min_non_zero_index = -1; + int max_index = -1; + int64_t observed_total_count = 0; + int i; + + for (i = 0; i < h->counts_len; i++) + { + int64_t count_at_index; + + if ((count_at_index = counts_get_direct(h, i)) > 0) + { + observed_total_count += count_at_index; + max_index = i; + if (min_non_zero_index == -1 && i != 0) + { + min_non_zero_index = i; + } + } + } + + if (max_index == -1) + { + h->max_value = 0; + } + else + { + int64_t max_value = hdr_value_at_index(h, max_index); + h->max_value = highest_equivalent_value(h, max_value); + } + + if (min_non_zero_index == -1) + { + h->min_value = INT64_MAX; + } + else + { + h->min_value = hdr_value_at_index(h, min_non_zero_index); + } + + h->total_count = observed_total_count; +} + +static int32_t buckets_needed_to_cover_value(int64_t value, int32_t sub_bucket_count, int32_t unit_magnitude) +{ + int64_t smallest_untrackable_value = ((int64_t) sub_bucket_count) << unit_magnitude; + int32_t buckets_needed = 1; + while (smallest_untrackable_value <= value) + { + if (smallest_untrackable_value > INT64_MAX / 2) + { + return buckets_needed + 1; + } + smallest_untrackable_value <<= 1; + buckets_needed++; + } + + return buckets_needed; +} + +// ## ## ######## ## ## ####### ######## ## ## +// ### ### ## ### ### ## ## ## ## ## ## +// #### #### ## #### #### ## ## ## ## #### +// ## ### ## ###### ## ### ## ## ## ######## ## +// ## ## ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## ## ## +// ## ## ######## ## ## ####### ## ## ## + +int hdr_calculate_bucket_config( + int64_t lowest_trackable_value, + int64_t highest_trackable_value, + int significant_figures, + struct hdr_histogram_bucket_config* cfg) +{ + if (lowest_trackable_value < 1 || + significant_figures < 1 || 5 < significant_figures) + { + return EINVAL; + } + else if (lowest_trackable_value * 2 > highest_trackable_value) + { + return EINVAL; + } + + cfg->lowest_trackable_value = lowest_trackable_value; + cfg->significant_figures = significant_figures; + cfg->highest_trackable_value = highest_trackable_value; + + int64_t largest_value_with_single_unit_resolution = 2 * power(10, significant_figures); + int32_t sub_bucket_count_magnitude = (int32_t) ceil(log((double)largest_value_with_single_unit_resolution) / log(2)); + cfg->sub_bucket_half_count_magnitude = ((sub_bucket_count_magnitude > 1) ? sub_bucket_count_magnitude : 1) - 1; + + cfg->unit_magnitude = (int32_t) floor(log((double)lowest_trackable_value) / log(2)); + + cfg->sub_bucket_count = (int32_t) pow(2, (cfg->sub_bucket_half_count_magnitude + 1)); + cfg->sub_bucket_half_count = cfg->sub_bucket_count / 2; + cfg->sub_bucket_mask = ((int64_t) cfg->sub_bucket_count - 1) << cfg->unit_magnitude; + + // determine exponent range needed to support the trackable value with no overflow: + cfg->bucket_count = buckets_needed_to_cover_value(highest_trackable_value, cfg->sub_bucket_count, (int32_t)cfg->unit_magnitude); + cfg->counts_len = (cfg->bucket_count + 1) * (cfg->sub_bucket_count / 2); + + return 0; +} + +void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg) +{ + h->lowest_trackable_value = cfg->lowest_trackable_value; + h->highest_trackable_value = cfg->highest_trackable_value; + h->unit_magnitude = (int32_t)cfg->unit_magnitude; + h->significant_figures = (int32_t)cfg->significant_figures; + h->sub_bucket_half_count_magnitude = cfg->sub_bucket_half_count_magnitude; + h->sub_bucket_half_count = cfg->sub_bucket_half_count; + h->sub_bucket_mask = cfg->sub_bucket_mask; + h->sub_bucket_count = cfg->sub_bucket_count; + h->min_value = INT64_MAX; + h->max_value = 0; + h->normalizing_index_offset = 0; + h->conversion_ratio = 1.0; + h->bucket_count = cfg->bucket_count; + h->counts_len = cfg->counts_len; + h->total_count = 0; +} + +int hdr_init( + int64_t lowest_trackable_value, + int64_t highest_trackable_value, + int significant_figures, + struct hdr_histogram** result) +{ + struct hdr_histogram_bucket_config cfg; + + int r = hdr_calculate_bucket_config(lowest_trackable_value, highest_trackable_value, significant_figures, &cfg); + if (r) + { + return r; + } + + size_t histogram_size = sizeof(struct hdr_histogram) + cfg.counts_len * sizeof(int64_t); + struct hdr_histogram* histogram = malloc(histogram_size); + + if (!histogram) + { + return ENOMEM; + } + + // memset will ensure that all of the function pointers are null. + memset((void*) histogram, 0, histogram_size); + + hdr_init_preallocated(histogram, &cfg); + *result = histogram; + + return 0; +} + + +int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result) +{ + return hdr_init(1, highest_trackable_value, significant_figures, result); +} + +// reset a histogram to zero. +void hdr_reset(struct hdr_histogram *h) +{ + h->total_count=0; + h->min_value = INT64_MAX; + h->max_value = 0; + memset((void *) &h->counts, 0, (sizeof(int64_t) * h->counts_len)); + return; +} + +size_t hdr_get_memory_size(struct hdr_histogram *h) +{ + return sizeof(struct hdr_histogram) + h->counts_len * sizeof(int64_t); +} + +// ## ## ######## ######## ### ######## ######## ###### +// ## ## ## ## ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## ## ## ## +// ## ## ######## ## ## ## ## ## ###### ###### +// ## ## ## ## ## ######### ## ## ## +// ## ## ## ## ## ## ## ## ## ## ## +// ####### ## ######## ## ## ## ######## ###### + + +bool hdr_record_value(struct hdr_histogram* h, int64_t value) +{ + return hdr_record_values(h, value, 1); +} + +bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count) +{ + if (value < 0) + { + return false; + } + + int32_t counts_index = counts_index_for(h, value); + + if (counts_index < 0 || h->counts_len <= counts_index) + { + return false; + } + + counts_inc_normalised(h, counts_index, count); + update_min_max(h, value); + + return true; +} + +bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expected_interval) +{ + return hdr_record_corrected_values(h, value, 1, expected_interval); +} + + +bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval) +{ + if (!hdr_record_values(h, value, count)) + { + return false; + } + + if (expected_interval <= 0 || value <= expected_interval) + { + return true; + } + + int64_t missing_value = value - expected_interval; + for (; missing_value >= expected_interval; missing_value -= expected_interval) + { + if (!hdr_record_values(h, missing_value, count)) + { + return false; + } + } + + return true; +} + +int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from) +{ + struct hdr_iter iter; + hdr_iter_recorded_init(&iter, from); + int64_t dropped = 0; + + while (hdr_iter_next(&iter)) + { + int64_t value = iter.value; + int64_t count = iter.count; + + if (!hdr_record_values(h, value, count)) + { + dropped += count; + } + } + + return dropped; +} + +int64_t hdr_add_while_correcting_for_coordinated_omission( + struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval) +{ + struct hdr_iter iter; + hdr_iter_recorded_init(&iter, from); + int64_t dropped = 0; + + while (hdr_iter_next(&iter)) + { + int64_t value = iter.value; + int64_t count = iter.count; + + if (!hdr_record_corrected_values(h, value, count, expected_interval)) + { + dropped += count; + } + } + + return dropped; +} + + + +// ## ## ### ## ## ## ######## ###### +// ## ## ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ###### ###### +// ## ## ######### ## ## ## ## ## +// ## ## ## ## ## ## ## ## ## ## +// ### ## ## ######## ####### ######## ###### + + +int64_t hdr_max(const struct hdr_histogram* h) +{ + if (0 == h->max_value) + { + return 0; + } + + return highest_equivalent_value(h, h->max_value); +} + +int64_t hdr_min(const struct hdr_histogram* h) +{ + if (0 < hdr_count_at_index(h, 0)) + { + return 0; + } + + return non_zero_min(h); +} + +int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile) +{ + struct hdr_iter iter; + hdr_iter_init(&iter, h); + + double requested_percentile = percentile < 100.0 ? percentile : 100.0; + int64_t count_at_percentile = + (int64_t) (((requested_percentile / 100) * h->total_count) + 0.5); + count_at_percentile = count_at_percentile > 1 ? count_at_percentile : 1; + int64_t total = 0; + + while (hdr_iter_next(&iter)) + { + total += iter.count; + + if (total >= count_at_percentile) + { + int64_t value_from_index = iter.value; + return highest_equivalent_value(h, value_from_index); + } + } + + return 0; +} + +double hdr_mean(const struct hdr_histogram* h) +{ + struct hdr_iter iter; + int64_t total = 0; + + hdr_iter_init(&iter, h); + + while (hdr_iter_next(&iter)) + { + if (0 != iter.count) + { + total += iter.count * hdr_median_equivalent_value(h, iter.value); + } + } + + return (total * 1.0) / h->total_count; +} + +double hdr_stddev(const struct hdr_histogram* h) +{ + double mean = hdr_mean(h); + double geometric_dev_total = 0.0; + + struct hdr_iter iter; + hdr_iter_init(&iter, h); + + while (hdr_iter_next(&iter)) + { + if (0 != iter.count) + { + double dev = (hdr_median_equivalent_value(h, iter.value) * 1.0) - mean; + geometric_dev_total += (dev * dev) * iter.count; + } + } + + return sqrt(geometric_dev_total / h->total_count); +} + +bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b) +{ + return lowest_equivalent_value(h, a) == lowest_equivalent_value(h, b); +} + +int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value) +{ + return lowest_equivalent_value(h, value); +} + +int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value) +{ + return counts_get_normalised(h, counts_index_for(h, value)); +} + +int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index) +{ + return counts_get_normalised(h, index); +} + + +// #### ######## ######## ######## ### ######## ####### ######## ###### +// ## ## ## ## ## ## ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## ## ## ## ## ## +// ## ## ###### ######## ## ## ## ## ## ######## ###### +// ## ## ## ## ## ######### ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## ## ## ## ## ## ## +// #### ## ######## ## ## ## ## ## ####### ## ## ###### + + +static bool has_buckets(struct hdr_iter* iter) +{ + return iter->counts_index < iter->h->counts_len; +} + +static bool has_next(struct hdr_iter* iter) +{ + return iter->cumulative_count < iter->total_count; +} + +static bool move_next(struct hdr_iter* iter) +{ + iter->counts_index++; + + if (!has_buckets(iter)) + { + return false; + } + + iter->count = counts_get_normalised(iter->h, iter->counts_index); + iter->cumulative_count += iter->count; + + iter->value = hdr_value_at_index(iter->h, iter->counts_index); + iter->highest_equivalent_value = highest_equivalent_value(iter->h, iter->value); + iter->lowest_equivalent_value = lowest_equivalent_value(iter->h, iter->value); + iter->median_equivalent_value = hdr_median_equivalent_value(iter->h, iter->value); + + return true; +} + +static int64_t peek_next_value_from_index(struct hdr_iter* iter) +{ + return hdr_value_at_index(iter->h, iter->counts_index + 1); +} + +static bool next_value_greater_than_reporting_level_upper_bound( + struct hdr_iter *iter, int64_t reporting_level_upper_bound) +{ + if (iter->counts_index >= iter->h->counts_len) + { + return false; + } + + return peek_next_value_from_index(iter) > reporting_level_upper_bound; +} + +static bool _basic_iter_next(struct hdr_iter *iter) +{ + if (!has_next(iter)) + { + return false; + } + + move_next(iter); + + return true; +} + +static void _update_iterated_values(struct hdr_iter* iter, int64_t new_value_iterated_to) +{ + iter->value_iterated_from = iter->value_iterated_to; + iter->value_iterated_to = new_value_iterated_to; +} + +static bool _all_values_iter_next(struct hdr_iter* iter) +{ + bool result = move_next(iter); + + if (result) + { + _update_iterated_values(iter, iter->value); + } + + return result; +} + +void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h) +{ + iter->h = h; + + iter->counts_index = -1; + iter->total_count = h->total_count; + iter->count = 0; + iter->cumulative_count = 0; + iter->value = 0; + iter->highest_equivalent_value = 0; + iter->value_iterated_from = 0; + iter->value_iterated_to = 0; + + iter->_next_fp = _all_values_iter_next; +} + +bool hdr_iter_next(struct hdr_iter* iter) +{ + return iter->_next_fp(iter); +} + +// ######## ######## ######## ###### ######## ## ## ######## #### ## ######## ###### +// ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## #### ## ## ## ## ## ## +// ######## ###### ######## ## ###### ## ## ## ## ## ## ###### ###### +// ## ## ## ## ## ## ## #### ## ## ## ## ## +// ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## +// ## ######## ## ## ###### ######## ## ## ## #### ######## ######## ###### + +static bool _percentile_iter_next(struct hdr_iter* iter) +{ + struct hdr_iter_percentiles* percentiles = &iter->specifics.percentiles; + + if (!has_next(iter)) + { + if (percentiles->seen_last_value) + { + return false; + } + + percentiles->seen_last_value = true; + percentiles->percentile = 100.0; + + return true; + } + + if (iter->counts_index == -1 && !_basic_iter_next(iter)) + { + return false; + } + + do + { + double current_percentile = (100.0 * (double) iter->cumulative_count) / iter->h->total_count; + if (iter->count != 0 && + percentiles->percentile_to_iterate_to <= current_percentile) + { + _update_iterated_values(iter, highest_equivalent_value(iter->h, iter->value)); + + percentiles->percentile = percentiles->percentile_to_iterate_to; + int64_t temp = (int64_t)(log(100 / (100.0 - (percentiles->percentile_to_iterate_to))) / log(2)) + 1; + int64_t half_distance = (int64_t) pow(2, (double) temp); + int64_t percentile_reporting_ticks = percentiles->ticks_per_half_distance * half_distance; + percentiles->percentile_to_iterate_to += 100.0 / percentile_reporting_ticks; + + return true; + } + } + while (_basic_iter_next(iter)); + + return true; +} + +void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance) +{ + iter->h = h; + + hdr_iter_init(iter, h); + + iter->specifics.percentiles.seen_last_value = false; + iter->specifics.percentiles.ticks_per_half_distance = ticks_per_half_distance; + iter->specifics.percentiles.percentile_to_iterate_to = 0.0; + iter->specifics.percentiles.percentile = 0.0; + + iter->_next_fp = _percentile_iter_next; +} + +static void format_line_string(char* str, size_t len, int significant_figures, format_type format) +{ +#if defined(_MSC_VER) +#define snprintf _snprintf +#pragma warning(push) +#pragma warning(disable: 4996) +#endif + const char* format_str = "%s%d%s"; + + switch (format) + { + case CSV: + snprintf(str, len, format_str, "%.", significant_figures, "f,%f,%d,%.2f\n"); + break; + case CLASSIC: + snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n"); + break; + default: + snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n"); + } +#if defined(_MSC_VER) +#undef snprintf +#pragma warning(pop) +#endif +} + + +// ######## ######## ###### ####### ######## ######## ######## ######## +// ## ## ## ## ## ## ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## ## ## ## ## ## +// ######## ###### ## ## ## ######## ## ## ###### ## ## +// ## ## ## ## ## ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## ## ## ## ## ## ## +// ## ## ######## ###### ####### ## ## ######## ######## ######## + + +static bool _recorded_iter_next(struct hdr_iter* iter) +{ + while (_basic_iter_next(iter)) + { + if (iter->count != 0) + { + _update_iterated_values(iter, iter->value); + + iter->specifics.recorded.count_added_in_this_iteration_step = iter->count; + return true; + } + } + + return false; +} + +void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h) +{ + hdr_iter_init(iter, h); + + iter->specifics.recorded.count_added_in_this_iteration_step = 0; + + iter->_next_fp = _recorded_iter_next; +} + +// ## #### ## ## ######## ### ######## +// ## ## ### ## ## ## ## ## ## +// ## ## #### ## ## ## ## ## ## +// ## ## ## ## ## ###### ## ## ######## +// ## ## ## #### ## ######### ## ## +// ## ## ## ### ## ## ## ## ## +// ######## #### ## ## ######## ## ## ## ## + + +static bool _iter_linear_next(struct hdr_iter* iter) +{ + struct hdr_iter_linear* linear = &iter->specifics.linear; + + linear->count_added_in_this_iteration_step = 0; + + if (has_next(iter) || + next_value_greater_than_reporting_level_upper_bound( + iter, linear->next_value_reporting_level_lowest_equivalent)) + { + do + { + if (iter->value >= linear->next_value_reporting_level_lowest_equivalent) + { + _update_iterated_values(iter, linear->next_value_reporting_level); + + linear->next_value_reporting_level += linear->value_units_per_bucket; + linear->next_value_reporting_level_lowest_equivalent = + lowest_equivalent_value(iter->h, linear->next_value_reporting_level); + + return true; + } + + if (!move_next(iter)) + { + return true; + } + + linear->count_added_in_this_iteration_step += iter->count; + } + while (true); + } + + return false; +} + + +void hdr_iter_linear_init(struct hdr_iter* iter, const struct hdr_histogram* h, int64_t value_units_per_bucket) +{ + hdr_iter_init(iter, h); + + iter->specifics.linear.count_added_in_this_iteration_step = 0; + iter->specifics.linear.value_units_per_bucket = value_units_per_bucket; + iter->specifics.linear.next_value_reporting_level = value_units_per_bucket; + iter->specifics.linear.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_per_bucket); + + iter->_next_fp = _iter_linear_next; +} + +// ## ####### ###### ### ######## #### ######## ## ## ## ## #### ###### +// ## ## ## ## ## ## ## ## ## ## ## ## ## ### ### ## ## ## +// ## ## ## ## ## ## ## ## ## ## ## ## #### #### ## ## +// ## ## ## ## #### ## ## ######## ## ## ######### ## ### ## ## ## +// ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ## +// ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## +// ######## ####### ###### ## ## ## ## #### ## ## ## ## ## #### ###### + +static bool _log_iter_next(struct hdr_iter *iter) +{ + struct hdr_iter_log* logarithmic = &iter->specifics.log; + + logarithmic->count_added_in_this_iteration_step = 0; + + if (has_next(iter) || + next_value_greater_than_reporting_level_upper_bound( + iter, logarithmic->next_value_reporting_level_lowest_equivalent)) + { + do + { + if (iter->value >= logarithmic->next_value_reporting_level_lowest_equivalent) + { + _update_iterated_values(iter, logarithmic->next_value_reporting_level); + + logarithmic->next_value_reporting_level *= (int64_t)logarithmic->log_base; + logarithmic->next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(iter->h, logarithmic->next_value_reporting_level); + + return true; + } + + if (!move_next(iter)) + { + return true; + } + + logarithmic->count_added_in_this_iteration_step += iter->count; + } + while (true); + } + + return false; +} + +void hdr_iter_log_init( + struct hdr_iter* iter, + const struct hdr_histogram* h, + int64_t value_units_first_bucket, + double log_base) +{ + hdr_iter_init(iter, h); + iter->specifics.log.count_added_in_this_iteration_step = 0; + iter->specifics.log.log_base = log_base; + iter->specifics.log.next_value_reporting_level = value_units_first_bucket; + iter->specifics.log.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_first_bucket); + + iter->_next_fp = _log_iter_next; +} + +// Printing. + +static const char* format_head_string(format_type format) +{ + switch (format) + { + case CSV: + return "%s,%s,%s,%s\n"; + case CLASSIC: + return "%12s %12s %12s %12s\n\n"; + default: + return "%12s %12s %12s %12s\n\n"; + } +} + +static const char CLASSIC_FOOTER[] = + "#[Mean = %12.3f, StdDeviation = %12.3f]\n" + "#[Max = %12.3f, Total count = %12" PRIu64 "]\n" + "#[Buckets = %12d, SubBuckets = %12d]\n"; + +int hdr_percentiles_print( + struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance, + double value_scale, format_type format) +{ + char line_format[25]; + format_line_string(line_format, 25, h->significant_figures, format); + const char* head_format = format_head_string(format); + int rc = 0; + + struct hdr_iter iter; + hdr_iter_percentile_init(&iter, h, ticks_per_half_distance); + + if (fprintf( + stream, head_format, + "Value", "Percentile", "TotalCount", "1/(1-Percentile)") < 0) + { + rc = EIO; + goto cleanup; + } + + struct hdr_iter_percentiles * percentiles = &iter.specifics.percentiles; + while (hdr_iter_next(&iter)) + { + double value = iter.highest_equivalent_value / value_scale; + double percentile = percentiles->percentile / 100.0; + int64_t total_count = iter.cumulative_count; + double inverted_percentile = (1.0 / (1.0 - percentile)); + + if (fprintf( + stream, line_format, value, percentile, total_count, inverted_percentile) < 0) + { + rc = EIO; + goto cleanup; + } + } + + if (CLASSIC == format) + { + double mean = hdr_mean(h) / value_scale; + double stddev = hdr_stddev(h) / value_scale; + double max = hdr_max(h) / value_scale; + + if (fprintf( + stream, CLASSIC_FOOTER, mean, stddev, max, + h->total_count, h->bucket_count, h->sub_bucket_count) < 0) + { + rc = EIO; + goto cleanup; + } + } + + cleanup: + return rc; +} |