/** * hdr_histogram.c * Written by Michael Barker and released to the public domain, * as explained at http://creativecommons.org/publicdomain/zero/1.0/ */ #include #include #include #include #include #include #include #include #include "hdr_histogram.h" #include "hdr_tests.h" #include "hdr_atomic.h" #ifndef HDR_MALLOC_INCLUDE #define HDR_MALLOC_INCLUDE "hdr_malloc.h" #endif #include HDR_MALLOC_INCLUDE /* ###### ####### ## ## ## ## ######## ###### */ /* ## ## ## ## ## ## ### ## ## ## ## */ /* ## ## ## ## ## #### ## ## ## */ /* ## ## ## ## ## ## ## ## ## ###### */ /* ## ## ## ## ## ## #### ## ## */ /* ## ## ## ## ## ## ## ### ## ## ## */ /* ###### ####### ####### ## ## ## ###### */ static int32_t normalize_index(const struct hdr_histogram* h, int32_t index) { int32_t normalized_index; int32_t adjustment = 0; if (h->normalizing_index_offset == 0) { return index; } normalized_index = index - h->normalizing_index_offset; 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 counts_inc_normalised_atomic( struct hdr_histogram* h, int32_t index, int64_t value) { int32_t normalised_index = normalize_index(h, index); hdr_atomic_add_fetch_64(&h->counts[normalised_index], value); hdr_atomic_add_fetch_64(&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 void update_min_max_atomic(struct hdr_histogram* h, int64_t value) { int64_t current_min_value; int64_t current_max_value; do { current_min_value = hdr_atomic_load_64(&h->min_value); if (0 == value || current_min_value <= value) { break; } } while (!hdr_atomic_compare_exchange_64(&h->min_value, ¤t_min_value, value)); do { current_max_value = hdr_atomic_load_64(&h->max_value); if (value <= current_max_value) { break; } } while (!hdr_atomic_compare_exchange_64(&h->max_value, ¤t_max_value, 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) # if defined(_WIN64) # pragma intrinsic(_BitScanReverse64) # else # pragma intrinsic(_BitScanReverse) # endif #endif static int32_t count_leading_zeros_64(int64_t value) { #if defined(_MSC_VER) uint32_t leading_zero = 0; #if defined(_WIN64) _BitScanReverse64(&leading_zero, value); #else uint32_t high = value >> 32; if (_BitScanReverse(&leading_zero, high)) { leading_zero += 32; } else { uint32_t low = value & 0x00000000FFFFFFFF; _BitScanReverse(&leading_zero, low); } #endif return 63 - leading_zero; /* smallest power of 2 containing value */ #else return __builtin_clzll(value); /* smallest power of 2 containing value */ #endif } static int32_t get_bucket_index(const struct hdr_histogram* h, int64_t value) { int32_t pow2ceiling = 64 - count_leading_zeros_64(value | h->sub_bucket_mask); /* smallest power of 2 containing value */ 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 size_of_equivalent_value_range_given_bucket_indices( const struct hdr_histogram *h, int32_t bucket_index, int32_t sub_bucket_index) { const 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); } static int64_t lowest_equivalent_value_given_bucket_indices( const struct hdr_histogram *h, int32_t bucket_index, int32_t sub_bucket_index) { 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_discernible_value, int64_t highest_trackable_value, int significant_figures, struct hdr_histogram_bucket_config* cfg) { int32_t sub_bucket_count_magnitude; int64_t largest_value_with_single_unit_resolution; if (lowest_discernible_value < 1 || significant_figures < 1 || 5 < significant_figures || lowest_discernible_value * 2 > highest_trackable_value) { return EINVAL; } cfg->lowest_discernible_value = lowest_discernible_value; cfg->significant_figures = significant_figures; cfg->highest_trackable_value = highest_trackable_value; largest_value_with_single_unit_resolution = 2 * power(10, significant_figures); 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; double unit_magnitude = log((double)lowest_discernible_value) / log(2); if (INT32_MAX < unit_magnitude) { return EINVAL; } cfg->unit_magnitude = (int32_t) unit_magnitude; 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; if (cfg->unit_magnitude + cfg->sub_bucket_half_count_magnitude > 61) { return EINVAL; } 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_discernible_value = cfg->lowest_discernible_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_discernible_value, int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result) { int64_t* counts; struct hdr_histogram_bucket_config cfg; struct hdr_histogram* histogram; int r = hdr_calculate_bucket_config(lowest_discernible_value, highest_trackable_value, significant_figures, &cfg); if (r) { return r; } counts = (int64_t*) hdr_calloc((size_t) cfg.counts_len, sizeof(int64_t)); if (!counts) { return ENOMEM; } histogram = (struct hdr_histogram*) hdr_calloc(1, sizeof(struct hdr_histogram)); if (!histogram) { hdr_free(counts); return ENOMEM; } histogram->counts = counts; hdr_init_preallocated(histogram, &cfg); *result = histogram; return 0; } void hdr_close(struct hdr_histogram* h) { if (h) { hdr_free(h->counts); hdr_free(h); } } 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(h->counts, 0, (sizeof(int64_t) * h->counts_len)); } 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_value_atomic(struct hdr_histogram* h, int64_t value) { return hdr_record_values_atomic(h, value, 1); } bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count) { int32_t counts_index; if (value < 0) { return false; } 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_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count) { int32_t counts_index; if (value < 0) { return false; } counts_index = counts_index_for(h, value); if (counts_index < 0 || h->counts_len <= counts_index) { return false; } counts_inc_normalised_atomic(h, counts_index, count); update_min_max_atomic(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_value_atomic(struct hdr_histogram* h, int64_t value, int64_t expected_interval) { return hdr_record_corrected_values_atomic(h, value, 1, expected_interval); } bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval) { int64_t missing_value; if (!hdr_record_values(h, value, count)) { return false; } if (expected_interval <= 0 || value <= expected_interval) { return true; } 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; } bool hdr_record_corrected_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval) { int64_t missing_value; if (!hdr_record_values_atomic(h, value, count)) { return false; } if (expected_interval <= 0 || value <= expected_interval) { return true; } missing_value = value - expected_interval; for (; missing_value >= expected_interval; missing_value -= expected_interval) { if (!hdr_record_values_atomic(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; int64_t dropped = 0; hdr_iter_recorded_init(&iter, from); 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; int64_t dropped = 0; hdr_iter_recorded_init(&iter, from); 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); } static int64_t get_value_from_idx_up_to_count(const struct hdr_histogram* h, int64_t count_at_percentile) { int64_t count_to_idx = 0; count_at_percentile = 0 < count_at_percentile ? count_at_percentile : 1; for (int32_t idx = 0; idx < h->counts_len; idx++) { count_to_idx += h->counts[idx]; if (count_to_idx >= count_at_percentile) { return hdr_value_at_index(h, idx); } } return 0; } int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile) { double requested_percentile = percentile < 100.0 ? percentile : 100.0; int64_t count_at_percentile = (int64_t) (((requested_percentile / 100) * h->total_count) + 0.5); int64_t value_from_idx = get_value_from_idx_up_to_count(h, count_at_percentile); if (percentile == 0.0) { return lowest_equivalent_value(h, value_from_idx); } return highest_equivalent_value(h, value_from_idx); } int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percentiles, int64_t *values, size_t length) { if (NULL == percentiles || NULL == values) { return EINVAL; } struct hdr_iter iter; const int64_t total_count = h->total_count; // to avoid allocations we use the values array for intermediate computation // i.e. to store the expected cumulative count at each percentile for (size_t i = 0; i < length; i++) { const double requested_percentile = percentiles[i] < 100.0 ? percentiles[i] : 100.0; const int64_t count_at_percentile = (int64_t) (((requested_percentile / 100) * total_count) + 0.5); values[i] = count_at_percentile > 1 ? count_at_percentile : 1; } hdr_iter_init(&iter, h); int64_t total = 0; size_t at_pos = 0; while (hdr_iter_next(&iter) && at_pos < length) { total += iter.count; while (at_pos < length && total >= values[at_pos]) { values[at_pos] = highest_equivalent_value(h, iter.value); at_pos++; } } 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; const int64_t value = hdr_value_at_index(iter->h, iter->counts_index); const int32_t bucket_index = get_bucket_index(iter->h, value); const int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, iter->h->unit_magnitude); const int64_t leq = lowest_equivalent_value_given_bucket_indices(iter->h, bucket_index, sub_bucket_index); const int64_t size_of_equivalent_value_range = size_of_equivalent_value_range_given_bucket_indices( iter->h, bucket_index, sub_bucket_index); iter->lowest_equivalent_value = leq; iter->value = value; iter->highest_equivalent_value = leq + size_of_equivalent_value_range - 1; iter->median_equivalent_value = leq + (size_of_equivalent_value_range >> 1); 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) || iter->counts_index >= iter->h->counts_len) { 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) { int64_t temp, half_distance, percentile_reporting_ticks; 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; temp = (int64_t)(log(100 / (100.0 - (percentiles->percentile_to_iterate_to))) / log(2)) + 1; half_distance = (int64_t) pow(2, (double) temp); 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; } void hdr_iter_linear_set_value_units_per_bucket(struct hdr_iter* iter, int64_t value_units_per_bucket) { iter->specifics.linear.value_units_per_bucket = value_units_per_bucket; } /* ## ####### ###### ### ######## #### ######## ## ## ## ## #### ###### */ /* ## ## ## ## ## ## ## ## ## ## ## ## ## ### ### ## ## ## */ /* ## ## ## ## ## ## ## ## ## ## ## ## #### #### ## ## */ /* ## ## ## ## #### ## ## ######## ## ## ######### ## ### ## ## ## */ /* ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ## */ /* ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## */ /* ######## ####### ###### ## ## ## ## #### ## ## ## ## ## #### ###### */ 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: 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]; const char* head_format; int rc = 0; struct hdr_iter iter; struct hdr_iter_percentiles * percentiles; format_line_string(line_format, 25, h->significant_figures, format); head_format = format_head_string(format); 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; } 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; }