diff --git a/deps/histogram/include/hdr/hdr_histogram.h b/deps/histogram/include/hdr/hdr_histogram.h index 3dd76037ee67..2c35186a152e 100644 --- a/deps/histogram/include/hdr/hdr_histogram.h +++ b/deps/histogram/include/hdr/hdr_histogram.h @@ -265,8 +265,9 @@ int64_t hdr_add_while_correcting_for_coordinated_omission( struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval); /** - * Get minimum value from the histogram. Will return 2^63-1 if the histogram - * is empty. + * Get minimum value from the histogram. Will return 0 if the histogram is + * empty, like hdr_max; a recorded 0 also gives 0, so use hdr_total_count to + * tell the two apart. * * @param h "This" pointer */ @@ -292,7 +293,10 @@ int64_t hdr_total_count(const struct hdr_histogram* h); * Get the value at a specific percentile. * * @param h "This" pointer. - * @param percentile The percentile to get the value for + * @param percentile The percentile to get the value for. Above 100 gives the value at + * 100; below 0 and -inf give the first recorded value. Exactly 0 returns the lowest + * equivalent value of that bucket. + * @return the value at the percentile, or 0 if no values have been recorded. */ int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile); @@ -300,11 +304,14 @@ int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile * Get the values at the given percentiles. * * @param h "This" pointer. - * @param percentiles The ordered percentiles array to get the values for. + * @param percentiles The ordered percentiles array to get the values for, with the + * same out-of-range handling as hdr_value_at_percentile. * @param length Number of elements in the arrays. * @param values Destination array containing the values at the given percentiles. * The values array should be allocated by the caller. - * @return 0 on success, ENOMEM if the provided destination array is null. + * Percentiles that cannot be resolved get the same value hdr_value_at_percentile + * returns for them, so all are 0 on an empty histogram. + * @return 0 on success, EINVAL if percentiles or values is null. */ int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percentiles, int64_t *values, size_t length); @@ -312,7 +319,7 @@ int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percen * Gets the standard deviation for the values in the histogram. * * @param h "This" pointer - * @return The standard deviation + * @return The standard deviation, or 0.0 if the histogram is empty. */ double hdr_stddev(const struct hdr_histogram* h); @@ -320,7 +327,7 @@ double hdr_stddev(const struct hdr_histogram* h); * Gets the mean for the values in the histogram. * * @param h "This" pointer - * @return The mean + * @return The mean, or 0.0 if the histogram is empty. */ double hdr_mean(const struct hdr_histogram* h); diff --git a/deps/histogram/include/hdr/hdr_histogram_version.h b/deps/histogram/include/hdr/hdr_histogram_version.h index c009dce9f205..a03cb6215a74 100644 --- a/deps/histogram/include/hdr/hdr_histogram_version.h +++ b/deps/histogram/include/hdr/hdr_histogram_version.h @@ -7,6 +7,6 @@ #ifndef HDR_HISTOGRAM_VERSION_H #define HDR_HISTOGRAM_VERSION_H -#define HDR_HISTOGRAM_VERSION "0.12.0" +#define HDR_HISTOGRAM_VERSION "0.12.1" #endif // HDR_HISTOGRAM_VERSION_H diff --git a/deps/histogram/src/hdr_histogram.c b/deps/histogram/src/hdr_histogram.c index 3d10cf589692..71c26c9e8d82 100644 --- a/deps/histogram/src/hdr_histogram.c +++ b/deps/histogram/src/hdr_histogram.c @@ -780,7 +780,7 @@ int64_t hdr_total_count(const struct hdr_histogram* h) int64_t hdr_min(const struct hdr_histogram* h) { - if (0 < hdr_count_at_index(h, 0)) + if (0 == hdr_total_count(h) || 0 < hdr_count_at_index(h, 0)) { return 0; } @@ -914,12 +914,25 @@ static int64_t get_value_from_idx_up_to_count(const struct hdr_histogram* h, int } +/* Target cumulative count in [1, total_count]; NaN/+inf -> total_count, -inf/negative -> 1 */ +static int64_t count_at_percentile(int64_t total_count, double percentile) +{ + const double requested = percentile < 100.0 ? percentile : 100.0; + const double cc = ((requested / 100) * (double) total_count) + 0.5; + if (!(cc >= 1.0)) return 1; + if (cc >= (double) total_count) return total_count; + return (int64_t) cc; +} + 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); + /* empty: the scan's not-found 0 must not be rounded up to highest_equivalent_value(0) */ + if (HDR_UNLIKELY(h->total_count == 0)) + { + return 0; + } + int64_t value_from_idx = + get_value_from_idx_up_to_count(h, count_at_percentile(h->total_count, percentile)); if (percentile == 0.0) { return lowest_equivalent_value(h, value_from_idx); @@ -927,12 +940,26 @@ int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile return highest_equivalent_value(h, value_from_idx); } +/* p0 is the bucket's lowest value, matching hdr_value_at_percentile */ +static int64_t percentile_bound(const struct hdr_histogram* h, double percentile, int64_t value) +{ + return percentile == 0.0 ? lowest_equivalent_value(h, value) : highest_equivalent_value(h, value); +} + 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; } + if (HDR_UNLIKELY(h->total_count == 0)) + { + for (size_t i = 0; i < length; i++) + { + values[i] = 0; + } + return 0; + } struct hdr_iter iter; const int64_t total_count = h->total_count; @@ -940,10 +967,7 @@ int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percen // 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; + values[i] = count_at_percentile(total_count, percentiles[i]); } uint64_t total = 0; /* unsigned: no signed-overflow UB when a hostile block sum is added at once */ @@ -974,7 +998,7 @@ int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percen total += (uint64_t)counts[j]; while (at_pos < length && (int64_t)total >= values[at_pos]) { - values[at_pos] = highest_equivalent_value(h, hdr_value_at_index(h, j)); + values[at_pos] = percentile_bound(h, percentiles[at_pos], hdr_value_at_index(h, j)); at_pos++; } } @@ -990,7 +1014,7 @@ int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percen total += (uint64_t)counts[idx]; while (at_pos < length && (int64_t)total >= values[at_pos]) { - values[at_pos] = highest_equivalent_value(h, hdr_value_at_index(h, idx)); + values[at_pos] = percentile_bound(h, percentiles[at_pos], hdr_value_at_index(h, idx)); at_pos++; } } @@ -1005,11 +1029,17 @@ int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percen total += (uint64_t)iter.count; while (at_pos < length && (int64_t)total >= values[at_pos]) { - values[at_pos] = highest_equivalent_value(h, iter.value); + values[at_pos] = percentile_bound(h, percentiles[at_pos], iter.value); at_pos++; } } } + /* unreached targets (counts below total_count) get the singular's not-found value */ + for (; at_pos < length; at_pos++) + { + values[at_pos] = percentiles[at_pos] == 0.0 ? + lowest_equivalent_value(h, 0) : highest_equivalent_value(h, 0); + } return 0; } @@ -1020,6 +1050,11 @@ double hdr_mean(const struct hdr_histogram* h) int64_t count = 0; int64_t total_count = h->total_count; + if (total_count == 0) + { + return 0.0; + } + hdr_iter_init(&iter, h); while (hdr_iter_next(&iter) && count < total_count) @@ -1037,6 +1072,11 @@ double hdr_mean(const struct hdr_histogram* h) double hdr_stddev(const struct hdr_histogram* h) { + if (h->total_count == 0) + { + return 0.0; + } + double mean = hdr_mean(h); double geometric_dev_total = 0.0;