blob: da3f7f702ce5d03526b1f76b2b09b4ced10946f3 [file] [log] [blame]
/**
* hdr_histogram.c
* Written by Michael Barker and released to the public domain,
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
*/
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#if defined(_MSC_VER)
#include <intrin.h>
#else
#include <inttypes.h>
#endif
#include "hdr_histogram.hpp"
/**
* Get the most significant bit set.
*
* The result is a one-based index, zero means no bits are set.
*/
inline int32_t hdr_bsr64(uint64_t x) {
#if defined(_MSC_VER)
unsigned long index;
# if defined(_M_AMD64)
const char isNonzero = _BitScanReverse64(&index, x);
if (isNonzero)
return index + 1;
else
return 0;
# else
// On 32-bit this needs to be split into two operations
char isNonzero = _BitScanReverse(&index, (unsigned long)(x >> 32));
if (isNonzero)
return index + 32 + 1;
else {
// Scan the last 32 bits by truncating the 64-bit value
isNonzero = _BitScanReverse(&index, (unsigned long)x);
if (isNonzero)
return index + 1;
else
return 0;
}
# endif
#else
if (x == 0)
return 0;
else
return 64 - (int32_t)__builtin_clzll(x);
#endif
}
// ###### ####### ## ## ## ## ######## ######
// ## ## ## ## ## ## ### ## ## ## ##
// ## ## ## ## ## #### ## ## ##
// ## ## ## ## ## ## ## ## ## ######
// ## ## ## ## ## ## #### ## ##
// ## ## ## ## ## ## ## ### ## ## ##
// ###### ####### ####### ## ## ## ######
static int32_t normalize_index(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(struct hdr_histogram* h, int32_t index)
{
return h->counts[index];
}
static int32_t counts_get_normalised(struct hdr_histogram* h, int32_t index)
{
return (int32_t)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_set_direct(struct hdr_histogram* h, int32_t index, int64_t value)
{
h->counts[index] = value;
}
static void counts_set_normalised(struct hdr_histogram* h, int32_t index, int64_t value)
{
int32_t normalised_index = normalize_index(h, index);
counts_set_direct(h, normalised_index, value);
}
static void counts_set_min_max(struct hdr_histogram* h, int64_t min, int64_t max)
{
h->min_value = min;
h->max_value = max;
}
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)
{
int result = 1;
while(exp)
{
result *= (int)base; exp--;
}
return result;
}
static int32_t get_bucket_index(struct hdr_histogram* h, int64_t value)
{
int32_t pow2ceiling = hdr_bsr64(value | h->sub_bucket_mask); // smallest power of 2 containing value
return pow2ceiling - (int32_t)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(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 int32_t counts_index_for(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, (int32_t)h->unit_magnitude);
return counts_index(h, bucket_index, sub_bucket_index);
}
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);
}
int64_t hdr_value_at_index(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, (int32_t)h->unit_magnitude);
}
static int64_t get_count_at_index(
struct hdr_histogram* h,
int32_t bucket_index,
int32_t sub_bucket_index)
{
return counts_get_normalised(h, counts_index(h, bucket_index, sub_bucket_index));
}
int64_t hdr_size_of_equivalent_value_range(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, (int32_t)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(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, (int32_t)h->unit_magnitude);
return value_from_index(bucket_index, sub_bucket_index, (int32_t)h->unit_magnitude);
}
int64_t hdr_next_non_equivalent_value(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(struct hdr_histogram* h, int64_t value)
{
return hdr_next_non_equivalent_value(h, value) - 1;
}
int64_t hdr_median_equivalent_value(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(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;
for (int 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;
}
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.0));
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.0));
cfg->sub_bucket_count = (int32_t) pow(2.0, (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 = cfg->unit_magnitude;
h->significant_figures = 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 = (struct hdr_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);
}
void shift_lowest_half_bucket_contents_left(struct hdr_histogram* h, int32_t shift_amount)
{
int32_t binary_orders_of_magnitude = shift_amount >> h->sub_bucket_half_count_magnitude;
for (int from_index = 1; from_index < h->sub_bucket_half_count; from_index++)
{
int64_t to_value = hdr_value_at_index(h, from_index) << binary_orders_of_magnitude;
int32_t to_index = counts_index_for(h, to_value);
int64_t count_at_from_index = counts_get_direct(h, from_index);
counts_set_normalised(h, to_index, count_at_from_index);
counts_set_direct(h, from_index, 0);
}
}
// TODO: Concurrency????
static void shift_normalizing_index_by_offset(struct hdr_histogram *h, int32_t shift_amount, bool populated)
{
int64_t zero_value_count = hdr_count_at_index(h, 0);
counts_set_normalised(h, 0, 0);
h->normalizing_index_offset += shift_amount;
if (populated)
{
shift_lowest_half_bucket_contents_left(h, shift_amount);
}
counts_set_normalised(h, 0, zero_value_count);
}
bool hdr_shift_values_left(struct hdr_histogram* h, int32_t binary_orders_of_magnitude)
{
if (binary_orders_of_magnitude < 0)
{
return false;
}
else if (binary_orders_of_magnitude == 0)
{
return true;
}
if (h->total_count == hdr_count_at_index(h, 0))
{
return true;
}
int32_t shift_amount = binary_orders_of_magnitude << h->sub_bucket_half_count_magnitude;
int32_t max_value_index = counts_index_for(h, hdr_max(h));
if (max_value_index >= (h->counts_len - shift_amount))
{
return false;
}
int64_t max_before_shift = h->max_value;
int64_t min_before_shift = h->min_value;
counts_set_min_max(h, INT64_MAX, 0);
bool lowest_half_bucket_populated = (min_before_shift < h->sub_bucket_half_count);
shift_normalizing_index_by_offset(h, shift_amount, lowest_half_bucket_populated);
update_min_max(h, max_before_shift << binary_orders_of_magnitude);
if (min_before_shift < INT64_MAX)
{
update_min_max(h, min_before_shift << binary_orders_of_magnitude);
}
return true;
}
bool hdr_shift_values_right(struct hdr_histogram* h, int32_t binary_orders_of_magnitude)
{
if (binary_orders_of_magnitude < 0)
{
return false;
}
else if (binary_orders_of_magnitude == 0)
{
return true;
}
if (h->total_count == hdr_count_at_index(h, 0))
{
return true;
}
int32_t shift_amount = h->sub_bucket_half_count * binary_orders_of_magnitude;
int32_t min_value_index = counts_index_for(h, non_zero_min(h));
if (min_value_index < shift_amount + h->sub_bucket_half_count)
{
return false;
}
int64_t max_value_before_shift = h->max_value;
int64_t min_value_before_shift = h->min_value;
counts_set_min_max(h, INT64_MAX, 0);
shift_normalizing_index_by_offset(h, -shift_amount, false);
update_min_max(h, max_value_before_shift >> binary_orders_of_magnitude);
if (min_value_before_shift < INT64_MAX)
{
update_min_max(h, min_value_before_shift >> binary_orders_of_magnitude);
}
return true;
}
// ## ## ######## ######## ### ######## ######## ######
// ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ## ##
// ## ## ######## ## ## ## ## ## ###### ######
// ## ## ## ## ## ######### ## ## ##
// ## ## ## ## ## ## ## ## ## ## ##
// ####### ## ######## ## ## ## ######## ######
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, 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_from_index;
int64_t count = iter.count_at_index;
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_from_index;
int64_t count = iter.count_at_index;
if (!hdr_record_corrected_values(h, value, count, expected_interval))
{
dropped += count;
}
}
return dropped;
}
// ## ## ### ## ## ## ######## ######
// ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ###### ######
// ## ## ######### ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ##
// ### ## ## ######## ####### ######## ######
int64_t hdr_max(struct hdr_histogram* h)
{
if (0 == h->max_value)
{
return 0;
}
return highest_equivalent_value(h, h->max_value);
}
int64_t hdr_min(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(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_at_index;
if (total >= count_at_percentile)
{
int64_t value_from_index = iter.value_from_index;
return highest_equivalent_value(h, value_from_index);
}
}
return 0;
}
double hdr_mean(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_at_index)
{
total += iter.count_at_index * hdr_median_equivalent_value(h, iter.value_from_index);
}
}
return (total * 1.0) / h->total_count;
}
double hdr_stddev(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_at_index)
{
double dev = (hdr_median_equivalent_value(h, iter.value_from_index) * 1.0) - mean;
geometric_dev_total += (dev * dev) * iter.count_at_index;
}
}
return sqrt(geometric_dev_total / h->total_count);
}
bool hdr_values_are_equivalent(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(struct hdr_histogram* h, int64_t value)
{
return lowest_equivalent_value(h, value);
}
int64_t hdr_count_at_value(struct hdr_histogram* h, int64_t value)
{
return counts_get_normalised(h, counts_index_for(h, value));
}
int64_t hdr_count_at_index(struct hdr_histogram* h, int32_t index)
{
return counts_get_normalised(h, index);
}
// #### ######## ######## ######## ### ######## ####### ######## ######
// ## ## ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ###### ######## ## ## ## ## ## ######## ######
// ## ## ## ## ## ######### ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ## ## ## ## ##
// #### ## ######## ## ## ## ## ## ####### ## ## ######
static bool has_buckets(struct hdr_iter* iter)
{
return iter->bucket_index < iter->h->bucket_count;
}
static bool has_next(struct hdr_iter* iter)
{
return iter->count_to_index < iter->h->total_count;
}
static void increment_bucket(struct hdr_histogram* h, int32_t* bucket_index, int32_t* sub_bucket_index)
{
(*sub_bucket_index)++;
if (*sub_bucket_index >= h->sub_bucket_count)
{
*sub_bucket_index = h->sub_bucket_half_count;
(*bucket_index)++;
}
}
static bool move_next(struct hdr_iter* iter)
{
increment_bucket(iter->h, &iter->bucket_index, &iter->sub_bucket_index);
if (!has_buckets(iter))
{
return false;
}
iter->count_at_index = get_count_at_index(iter->h, iter->bucket_index, iter->sub_bucket_index);
iter->count_to_index += iter->count_at_index;
iter->value_from_index = value_from_index(iter->bucket_index, iter->sub_bucket_index, (int32_t)iter->h->unit_magnitude);
iter->highest_equivalent_value = highest_equivalent_value(iter->h, iter->value_from_index);
return true;
}
static int64_t peek_next_value_from_index(struct hdr_iter* iter)
{
int32_t bucket_index = iter->bucket_index;
int32_t sub_bucket_index = iter->sub_bucket_index;
increment_bucket(iter->h, &bucket_index, &sub_bucket_index);
return value_from_index(bucket_index, sub_bucket_index, (int32_t)iter->h->unit_magnitude);
}
bool _basic_iter_next(struct hdr_iter *iter)
{
if (!has_next(iter))
{
return false;
}
if (!move_next(iter))
{
return false;
}
return true;
}
void hdr_iter_init(struct hdr_iter* itr, struct hdr_histogram* h)
{
itr->h = h;
itr->bucket_index = 0;
itr->sub_bucket_index = -1;
itr->count_at_index = 0;
itr->count_to_index = 0;
itr->value_from_index = 0;
itr->highest_equivalent_value = 0;
itr->_next_fp = _basic_iter_next;
}
bool hdr_iter_next(struct hdr_iter* iter)
{
return iter->_next_fp(iter);
}
// ######## ######## ######## ###### ######## ## ## ######## #### ## ######## ######
// ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## #### ## ## ## ## ## ##
// ######## ###### ######## ## ###### ## ## ## ## ## ## ###### ######
// ## ## ## ## ## ## ## #### ## ## ## ## ##
// ## ## ## ## ## ## ## ## ### ## ## ## ## ## ##
// ## ######## ## ## ###### ######## ## ## ## #### ######## ######## ######
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->sub_bucket_index == -1 && !_basic_iter_next(iter))
{
return false;
}
do
{
double current_percentile = (100.0 * (double) iter->count_to_index) / iter->h->total_count;
if (iter->count_at_index != 0 &&
percentiles->percentile_to_iterate_to <= current_percentile)
{
percentiles->percentile = percentiles->percentile_to_iterate_to;
int64_t half_distance = (int64_t) pow(2.0, (double)((int64_t)(log(100.0 / (100.0 - (percentiles->percentile_to_iterate_to))) / log(2.0)) + 1));
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, 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;
}
// ######## ######## ###### ####### ######## ######## ######## ########
// ## ## ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ## ## ## ##
// ######## ###### ## ## ## ######## ## ## ###### ## ##
// ## ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ######## ###### ####### ## ## ######## ######## ########
bool _recorded_iter_next(struct hdr_iter* iter)
{
while (_basic_iter_next(iter))
{
if (iter->count_at_index != 0)
{
iter->specifics.recorded.count_added_in_this_iteration_step = iter->count_at_index;
return true;
}
}
return false;
}
void hdr_iter_recorded_init(struct hdr_iter* iter, 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;
}
// ## #### ## ## ######## ### ########
// ## ## ### ## ## ## ## ## ##
// ## ## #### ## ## ## ## ## ##
// ## ## ## ## ## ###### ## ## ########
// ## ## ## #### ## ######### ## ##
// ## ## ## ### ## ## ## ## ##
// ######## #### ## ## ######## ## ## ## ##
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) ||
peek_next_value_from_index(iter) > linear->next_value_reporting_level_lowest_equivalent)
{
do
{
if (iter->value_from_index >= linear->next_value_reporting_level_lowest_equivalent)
{
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))
{
break;
}
linear->count_added_in_this_iteration_step += iter->count_at_index;
}
while (true);
}
return false;
}
void hdr_iter_linear_init(struct hdr_iter* iter, 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;
}
// ## ####### ###### ### ######## #### ######## ## ## ## ## #### ######
// ## ## ## ## ## ## ## ## ## ## ## ## ## ### ### ## ## ##
// ## ## ## ## ## ## ## ## ## ## ## ## #### #### ## ##
// ## ## ## ## #### ## ## ######## ## ## ######### ## ### ## ## ##
// ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
// ######## ####### ###### ## ## ## ## #### ## ## ## ## ## #### ######
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) ||
peek_next_value_from_index(iter) > logarithmic->next_value_reporting_level_lowest_equivalent)
{
do
{
if (iter->value_from_index >= logarithmic->next_value_reporting_level_lowest_equivalent)
{
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))
{
break;
}
logarithmic->count_added_in_this_iteration_step += iter->count_at_index;
}
while (true);
}
return false;
}
void hdr_iter_log_init(
struct hdr_iter* iter,
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.value_units_first_bucket = value_units_first_bucket;
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;
}