blob: 6d5ead9aa64b7ea74478ff768124f22b05851a77 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * 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
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * 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.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080016 *
17 */
18
19#include <grpc/support/histogram.h>
20
21#include <math.h>
22#include <stddef.h>
23#include <string.h>
24
25#include <grpc/support/alloc.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080026#include <grpc/support/log.h>
Craig Tillerf40df232016-03-25 13:38:14 -070027#include <grpc/support/port_platform.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080028#include <grpc/support/useful.h>
29
30/* Histograms are stored with exponentially increasing bucket sizes.
31 The first bucket is [0, m) where m = 1 + resolution
32 Bucket n (n>=1) contains [m**n, m**(n+1))
33 There are sufficient buckets to reach max_bucket_start */
34
35struct gpr_histogram {
36 /* Sum of all values seen so far */
37 double sum;
38 /* Sum of squares of all values seen so far */
39 double sum_of_squares;
40 /* number of values seen so far */
41 double count;
42 /* m in the description */
43 double multiplier;
44 double one_on_log_multiplier;
45 /* minimum value seen */
46 double min_seen;
47 /* maximum value seen */
48 double max_seen;
49 /* maximum representable value */
50 double max_possible;
51 /* number of buckets */
52 size_t num_buckets;
53 /* the buckets themselves */
Craig Tiller7536af02015-12-22 13:49:30 -080054 uint32_t *buckets;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080055};
56
57/* determine a bucket index given a value - does no bounds checking */
58static size_t bucket_for_unchecked(gpr_histogram *h, double x) {
59 return (size_t)(log(x) * h->one_on_log_multiplier);
60}
61
62/* bounds checked version of the above */
63static size_t bucket_for(gpr_histogram *h, double x) {
vjpai6db72242015-04-21 10:50:12 -070064 size_t bucket = bucket_for_unchecked(h, GPR_CLAMP(x, 1.0, h->max_possible));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080065 GPR_ASSERT(bucket < h->num_buckets);
66 return bucket;
67}
68
69/* at what value does a bucket start? */
70static double bucket_start(gpr_histogram *h, double x) {
71 return pow(h->multiplier, x);
72}
73
74gpr_histogram *gpr_histogram_create(double resolution,
75 double max_bucket_start) {
Craig Tiller61f86d92017-05-11 13:36:42 -070076 gpr_histogram *h = (gpr_histogram *)gpr_malloc(sizeof(gpr_histogram));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080077 GPR_ASSERT(resolution > 0.0);
78 GPR_ASSERT(max_bucket_start > resolution);
79 h->sum = 0.0;
80 h->sum_of_squares = 0.0;
81 h->multiplier = 1.0 + resolution;
82 h->one_on_log_multiplier = 1.0 / log(1.0 + resolution);
83 h->max_possible = max_bucket_start;
84 h->count = 0.0;
85 h->min_seen = max_bucket_start;
86 h->max_seen = 0.0;
87 h->num_buckets = bucket_for_unchecked(h, max_bucket_start) + 1;
88 GPR_ASSERT(h->num_buckets > 1);
89 GPR_ASSERT(h->num_buckets < 100000000);
Craig Tiller61f86d92017-05-11 13:36:42 -070090 h->buckets = (uint32_t *)gpr_zalloc(sizeof(uint32_t) * h->num_buckets);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080091 return h;
92}
93
94void gpr_histogram_destroy(gpr_histogram *h) {
95 gpr_free(h->buckets);
96 gpr_free(h);
97}
98
99void gpr_histogram_add(gpr_histogram *h, double x) {
100 h->sum += x;
101 h->sum_of_squares += x * x;
102 h->count++;
103 if (x < h->min_seen) {
104 h->min_seen = x;
105 }
106 if (x > h->max_seen) {
107 h->max_seen = x;
108 }
109 h->buckets[bucket_for(h, x)]++;
110}
111
vjpai119c1032015-10-29 01:21:04 -0700112int gpr_histogram_merge(gpr_histogram *dst, const gpr_histogram *src) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800113 if ((dst->num_buckets != src->num_buckets) ||
114 (dst->multiplier != src->multiplier)) {
115 /* Fail because these histograms don't match */
116 return 0;
117 }
Craig Tiller76877c32015-03-03 16:04:23 -0800118 gpr_histogram_merge_contents(dst, src->buckets, src->num_buckets,
119 src->min_seen, src->max_seen, src->sum,
120 src->sum_of_squares, src->count);
121 return 1;
122}
123
Craig Tiller7536af02015-12-22 13:49:30 -0800124void gpr_histogram_merge_contents(gpr_histogram *dst, const uint32_t *data,
Craig Tiller76877c32015-03-03 16:04:23 -0800125 size_t data_count, double min_seen,
126 double max_seen, double sum,
127 double sum_of_squares, double count) {
128 size_t i;
129 GPR_ASSERT(dst->num_buckets == data_count);
130 dst->sum += sum;
131 dst->sum_of_squares += sum_of_squares;
132 dst->count += count;
133 if (min_seen < dst->min_seen) {
134 dst->min_seen = min_seen;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800135 }
Craig Tiller76877c32015-03-03 16:04:23 -0800136 if (max_seen > dst->max_seen) {
137 dst->max_seen = max_seen;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800138 }
139 for (i = 0; i < dst->num_buckets; i++) {
Craig Tiller76877c32015-03-03 16:04:23 -0800140 dst->buckets[i] += data[i];
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800141 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800142}
143
144static double threshold_for_count_below(gpr_histogram *h, double count_below) {
145 double count_so_far;
146 double lower_bound;
147 double upper_bound;
jtattermusch98bffb72014-12-09 12:47:19 -0800148 size_t lower_idx;
149 size_t upper_idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800150
Craig Tiller80ca5162015-06-22 14:31:44 -0700151 if (h->count == 0) {
152 return 0.0;
153 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800154
155 if (count_below <= 0) {
156 return h->min_seen;
157 }
158 if (count_below >= h->count) {
159 return h->max_seen;
160 }
161
162 /* find the lowest bucket that gets us above count_below */
163 count_so_far = 0.0;
164 for (lower_idx = 0; lower_idx < h->num_buckets; lower_idx++) {
165 count_so_far += h->buckets[lower_idx];
166 if (count_so_far >= count_below) {
167 break;
168 }
169 }
170 if (count_so_far == count_below) {
171 /* this bucket hits the threshold exactly... we should be midway through
172 any run of zero values following the bucket */
173 for (upper_idx = lower_idx + 1; upper_idx < h->num_buckets; upper_idx++) {
174 if (h->buckets[upper_idx]) {
175 break;
176 }
177 }
Craig Tillerd6c98df2015-08-18 09:33:44 -0700178 return (bucket_start(h, (double)lower_idx) +
179 bucket_start(h, (double)upper_idx)) /
180 2.0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800181 } else {
182 /* treat values as uniform throughout the bucket, and find where this value
183 should lie */
murgatroid995e71d7a2015-06-19 12:24:44 -0700184 lower_bound = bucket_start(h, (double)lower_idx);
185 upper_bound = bucket_start(h, (double)(lower_idx + 1));
Craig Tillerd6c98df2015-08-18 09:33:44 -0700186 return GPR_CLAMP(upper_bound -
187 (upper_bound - lower_bound) *
188 (count_so_far - count_below) /
189 h->buckets[lower_idx],
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800190 h->min_seen, h->max_seen);
191 }
192}
193
194double gpr_histogram_percentile(gpr_histogram *h, double percentile) {
195 return threshold_for_count_below(h, h->count * percentile / 100.0);
196}
197
198double gpr_histogram_mean(gpr_histogram *h) {
sreek7d3ea592015-09-29 14:12:44 -0700199 GPR_ASSERT(h->count != 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800200 return h->sum / h->count;
201}
202
203double gpr_histogram_stddev(gpr_histogram *h) {
204 return sqrt(gpr_histogram_variance(h));
205}
206
207double gpr_histogram_variance(gpr_histogram *h) {
208 if (h->count == 0) return 0.0;
209 return (h->sum_of_squares * h->count - h->sum * h->sum) /
210 (h->count * h->count);
211}
212
213double gpr_histogram_maximum(gpr_histogram *h) { return h->max_seen; }
214
215double gpr_histogram_minimum(gpr_histogram *h) { return h->min_seen; }
216
217double gpr_histogram_count(gpr_histogram *h) { return h->count; }
218
219double gpr_histogram_sum(gpr_histogram *h) { return h->sum; }
220
221double gpr_histogram_sum_of_squares(gpr_histogram *h) {
222 return h->sum_of_squares;
Craig Tiller190d3602015-02-18 09:23:38 -0800223}
Craig Tiller76877c32015-03-03 16:04:23 -0800224
Craig Tiller7536af02015-12-22 13:49:30 -0800225const uint32_t *gpr_histogram_get_contents(gpr_histogram *h, size_t *size) {
Craig Tiller76877c32015-03-03 16:04:23 -0800226 *size = h->num_buckets;
227 return h->buckets;
Craig Tillerf2825142015-03-03 17:15:36 -0800228}