3 * Copyright 2015 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include "test/core/util/histogram.h"
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/log.h>
27 #include <grpc/support/port_platform.h>
29 #include "src/core/lib/gpr/useful.h"
31 /* Histograms are stored with exponentially increasing bucket sizes.
32 The first bucket is [0, m) where m = 1 + resolution
33 Bucket n (n>=1) contains [m**n, m**(n+1))
34 There are sufficient buckets to reach max_bucket_start */
36 struct grpc_histogram {
37 /* Sum of all values seen so far */
39 /* Sum of squares of all values seen so far */
40 double sum_of_squares;
41 /* number of values seen so far */
43 /* m in the description */
45 double one_on_log_multiplier;
46 /* minimum value seen */
48 /* maximum value seen */
50 /* maximum representable value */
52 /* number of buckets */
54 /* the buckets themselves */
58 /* determine a bucket index given a value - does no bounds checking */
59 static size_t bucket_for_unchecked(grpc_histogram* h, double x) {
60 return static_cast<size_t>(log(x) * h->one_on_log_multiplier);
63 /* bounds checked version of the above */
64 static size_t bucket_for(grpc_histogram* h, double x) {
65 size_t bucket = bucket_for_unchecked(h, GPR_CLAMP(x, 1.0, h->max_possible));
66 GPR_ASSERT(bucket < h->num_buckets);
70 /* at what value does a bucket start? */
71 static double bucket_start(grpc_histogram* h, double x) {
72 return pow(h->multiplier, x);
75 grpc_histogram* grpc_histogram_create(double resolution,
76 double max_bucket_start) {
78 static_cast<grpc_histogram*>(gpr_malloc(sizeof(grpc_histogram)));
79 GPR_ASSERT(resolution > 0.0);
80 GPR_ASSERT(max_bucket_start > resolution);
82 h->sum_of_squares = 0.0;
83 h->multiplier = 1.0 + resolution;
84 h->one_on_log_multiplier = 1.0 / log(1.0 + resolution);
85 h->max_possible = max_bucket_start;
87 h->min_seen = max_bucket_start;
89 h->num_buckets = bucket_for_unchecked(h, max_bucket_start) + 1;
90 GPR_ASSERT(h->num_buckets > 1);
91 GPR_ASSERT(h->num_buckets < 100000000);
93 static_cast<uint32_t*>(gpr_zalloc(sizeof(uint32_t) * h->num_buckets));
97 void grpc_histogram_destroy(grpc_histogram* h) {
102 void grpc_histogram_add(grpc_histogram* h, double x) {
104 h->sum_of_squares += x * x;
106 if (x < h->min_seen) {
109 if (x > h->max_seen) {
112 h->buckets[bucket_for(h, x)]++;
115 int grpc_histogram_merge(grpc_histogram* dst, const grpc_histogram* src) {
116 if ((dst->num_buckets != src->num_buckets) ||
117 (dst->multiplier != src->multiplier)) {
118 /* Fail because these histograms don't match */
121 grpc_histogram_merge_contents(dst, src->buckets, src->num_buckets,
122 src->min_seen, src->max_seen, src->sum,
123 src->sum_of_squares, src->count);
127 void grpc_histogram_merge_contents(grpc_histogram* histogram,
128 const uint32_t* data, size_t data_count,
129 double min_seen, double max_seen, double sum,
130 double sum_of_squares, double count) {
132 GPR_ASSERT(histogram->num_buckets == data_count);
133 histogram->sum += sum;
134 histogram->sum_of_squares += sum_of_squares;
135 histogram->count += count;
136 if (min_seen < histogram->min_seen) {
137 histogram->min_seen = min_seen;
139 if (max_seen > histogram->max_seen) {
140 histogram->max_seen = max_seen;
142 for (i = 0; i < histogram->num_buckets; i++) {
143 histogram->buckets[i] += data[i];
147 static double threshold_for_count_below(grpc_histogram* h, double count_below) {
158 if (count_below <= 0) {
161 if (count_below >= h->count) {
165 /* find the lowest bucket that gets us above count_below */
167 for (lower_idx = 0; lower_idx < h->num_buckets; lower_idx++) {
168 count_so_far += h->buckets[lower_idx];
169 if (count_so_far >= count_below) {
173 if (count_so_far == count_below) {
174 /* this bucket hits the threshold exactly... we should be midway through
175 any run of zero values following the bucket */
176 for (upper_idx = lower_idx + 1; upper_idx < h->num_buckets; upper_idx++) {
177 if (h->buckets[upper_idx]) {
181 return (bucket_start(h, static_cast<double>(lower_idx)) +
182 bucket_start(h, static_cast<double>(upper_idx))) /
185 /* treat values as uniform throughout the bucket, and find where this value
187 lower_bound = bucket_start(h, static_cast<double>(lower_idx));
188 upper_bound = bucket_start(h, static_cast<double>(lower_idx + 1));
189 return GPR_CLAMP(upper_bound - (upper_bound - lower_bound) *
190 (count_so_far - count_below) /
191 h->buckets[lower_idx],
192 h->min_seen, h->max_seen);
196 double grpc_histogram_percentile(grpc_histogram* h, double percentile) {
197 return threshold_for_count_below(h, h->count * percentile / 100.0);
200 double grpc_histogram_mean(grpc_histogram* h) {
201 GPR_ASSERT(h->count != 0);
202 return h->sum / h->count;
205 double grpc_histogram_stddev(grpc_histogram* h) {
206 return sqrt(grpc_histogram_variance(h));
209 double grpc_histogram_variance(grpc_histogram* h) {
210 if (h->count == 0) return 0.0;
211 return (h->sum_of_squares * h->count - h->sum * h->sum) /
212 (h->count * h->count);
215 double grpc_histogram_maximum(grpc_histogram* h) { return h->max_seen; }
217 double grpc_histogram_minimum(grpc_histogram* h) { return h->min_seen; }
219 double grpc_histogram_count(grpc_histogram* h) { return h->count; }
221 double grpc_histogram_sum(grpc_histogram* h) { return h->sum; }
223 double grpc_histogram_sum_of_squares(grpc_histogram* h) {
224 return h->sum_of_squares;
227 const uint32_t* grpc_histogram_get_contents(grpc_histogram* histogram,
229 *count = histogram->num_buckets;
230 return histogram->buckets;