diff --git a/deps/hdr_histogram/.dirstamp b/deps/hdr_histogram/.dirstamp new file mode 100644 index 0000000..e69de29 diff --git a/deps/hdr_histogram/hdr_histogram.c b/deps/hdr_histogram/hdr_histogram.c index 7bb6422..b3b48e0 100644 --- a/deps/hdr_histogram/hdr_histogram.c +++ b/deps/hdr_histogram/hdr_histogram.c @@ -643,28 +643,67 @@ int64_t hdr_min(const struct hdr_histogram* h) 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) { - struct hdr_iter iter; - int64_t total = 0; 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 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); +} - hdr_iter_init(&iter, h); +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; + } - while (hdr_iter_next(&iter)) + 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++) { - total += iter.count; + 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; + } - if (total >= count_at_percentile) + 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]) { - int64_t value_from_index = iter.value; - return highest_equivalent_value(h, value_from_index); + values[at_pos] = highest_equivalent_value(h, iter.value); + at_pos++; } } - return 0; } diff --git a/deps/hdr_histogram/hdr_histogram.h b/deps/hdr_histogram/hdr_histogram.h index dc35416..ad1e3a3 100644 --- a/deps/hdr_histogram/hdr_histogram.h +++ b/deps/hdr_histogram/hdr_histogram.h @@ -284,6 +284,18 @@ int64_t hdr_max(const struct hdr_histogram* h); */ 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 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. + */ +int hdr_value_at_percentiles(const struct hdr_histogram *h, const double *percentiles, int64_t *values, size_t length); + /** * Gets the standard deviation for the values in the histogram. * diff --git a/run_stats.cpp b/run_stats.cpp index f945377..a5e38a0 100644 --- a/run_stats.cpp +++ b/run_stats.cpp @@ -117,8 +117,9 @@ run_stats::run_stats(benchmark_config *config) : { memset(&m_start_time, 0, sizeof(m_start_time)); memset(&m_end_time, 0, sizeof(m_end_time)); - quantiles_list = config->print_percentiles.quantile_list; - + std::vector quantiles_list_float = config->print_percentiles.quantile_list; + std::sort(quantiles_list_float.begin(), quantiles_list_float.end()); + quantiles_list = std::vector(quantiles_list_float.begin(), quantiles_list_float.end()); if (config->arbitrary_commands->is_defined()) { setup_arbitrary_commands(config->arbitrary_commands->size()); } @@ -882,7 +883,7 @@ void run_stats::summarize(totals& result) const void result_print_to_json(json_handler * jsonhandler, const char * type, double ops_sec, double hits, double miss, double moved, double ask, double kbs, double kbs_rx, double kbs_tx, double latency, long m_total_latency, long ops, - std::vector quantile_list, struct hdr_histogram* latency_histogram, + std::vector quantile_list, struct hdr_histogram* latency_histogram, std::vector timestamps, std::vector timeserie_stats ) { diff --git a/run_stats.h b/run_stats.h index 580ef1f..14796ea 100644 --- a/run_stats.h +++ b/run_stats.h @@ -97,7 +97,7 @@ class run_stats { totals m_totals; std::list m_stats; - std::vector quantiles_list; + std::vector quantiles_list; // current second stats ( appended to m_stats and reset every second ) one_second_stats m_cur_stats; diff --git a/run_stats_types.cpp b/run_stats_types.cpp index 77068dd..7ae7b0e 100644 --- a/run_stats_types.cpp +++ b/run_stats_types.cpp @@ -72,11 +72,14 @@ void one_sec_cmd_stats::merge(const one_sec_cmd_stats& other) { m_min_latency = other.m_min_latency < m_min_latency ? other.m_min_latency : m_min_latency; } -void one_sec_cmd_stats::summarize_quantiles(safe_hdr_histogram histogram, std::vector quantiles) { - for (std::size_t i = 0; i < quantiles.size(); i++){ - const float quantile = quantiles[i]; - const double value = hdr_value_at_percentile(histogram, quantile)/ (double) LATENCY_HDR_RESULTS_MULTIPLIER; - summarized_quantile_values.push_back(value); +void one_sec_cmd_stats::summarize_quantiles(safe_hdr_histogram histogram, std::vector sorted_quantiles) { + std::vector values(sorted_quantiles.size()); + int result = hdr_value_at_percentiles(histogram, sorted_quantiles.data(), values.data(), sorted_quantiles.size()); + if (result != 0) { + return; + } + for (std::size_t i = 0; i < sorted_quantiles.size(); i++) { + summarized_quantile_values.push_back(values[i] / static_cast(LATENCY_HDR_RESULTS_MULTIPLIER)); } } diff --git a/run_stats_types.h b/run_stats_types.h index 827c86d..2b6e982 100644 --- a/run_stats_types.h +++ b/run_stats_types.h @@ -24,7 +24,7 @@ #define LATENCY_HDR_SIGDIGTS 3 #define LATENCY_HDR_SEC_MIN_VALUE 10 #define LATENCY_HDR_SEC_MAX_VALUE 600000000 ## LL -#define LATENCY_HDR_SEC_SIGDIGTS 3 +#define LATENCY_HDR_SEC_SIGDIGTS 2 #define LATENCY_HDR_RESULTS_MULTIPLIER 1000 #define LATENCY_HDR_GRANULARITY 10 @@ -90,7 +90,14 @@ class one_sec_cmd_stats { one_sec_cmd_stats(); void reset(); void merge(const one_sec_cmd_stats& other); - void summarize_quantiles(safe_hdr_histogram histogram, std::vector quantiles); + /** + * Summarizes quantiles from the given histogram. + * + * @param histogram The histogram from which quantile values are extracted. + * @param sorted_quantiles A sorted (ascending order) vector of quantiles for which values will be computed. + * The caller must ensure the vector is sorted from smallest to largest. + */ + void summarize_quantiles(safe_hdr_histogram histogram, std::vector sorted_quantiles); void update_op(unsigned int bytes_rx, unsigned int bytes_tx, unsigned int latency); void update_op(unsigned int bytes_rx, unsigned int bytes_tx, unsigned int latency, unsigned int hits, unsigned int misses); void update_moved_op(unsigned int bytes_rx, unsigned int bytes_tx, unsigned int latency);