• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
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
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
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.
16 //
17 //
18 
19 #include "test/core/test_util/histogram.h"
20 
21 #include <grpc/support/alloc.h>
22 #include <grpc/support/port_platform.h>
23 #include <math.h>
24 #include <stddef.h>
25 
26 #include "absl/log/check.h"
27 #include "src/core/util/useful.h"
28 
29 // Histograms are stored with exponentially increasing bucket sizes.
30 // The first bucket is [0, m) where m = 1 + resolution
31 // Bucket n (n>=1) contains [m**n, m**(n+1))
32 // There are sufficient buckets to reach max_bucket_start
33 
34 struct grpc_histogram {
35   // Sum of all values seen so far
36   double sum;
37   // Sum of squares of all values seen so far
38   double sum_of_squares;
39   // number of values seen so far
40   double count;
41   // m in the description
42   double multiplier;
43   double one_on_log_multiplier;
44   // minimum value seen
45   double min_seen;
46   // maximum value seen
47   double max_seen;
48   // maximum representable value
49   double max_possible;
50   // number of buckets
51   size_t num_buckets;
52   // the buckets themselves
53   uint32_t* buckets;
54 };
55 
56 // determine a bucket index given a value - does no bounds checking
bucket_for_unchecked(grpc_histogram * h,double x)57 static size_t bucket_for_unchecked(grpc_histogram* h, double x) {
58   return static_cast<size_t>(log(x) * h->one_on_log_multiplier);
59 }
60 
61 // bounds checked version of the above
bucket_for(grpc_histogram * h,double x)62 static size_t bucket_for(grpc_histogram* h, double x) {
63   size_t bucket =
64       bucket_for_unchecked(h, grpc_core::Clamp(x, 1.0, h->max_possible));
65   CHECK(bucket < h->num_buckets);
66   return bucket;
67 }
68 
69 // at what value does a bucket start?
bucket_start(grpc_histogram * h,double x)70 static double bucket_start(grpc_histogram* h, double x) {
71   return pow(h->multiplier, x);
72 }
73 
grpc_histogram_create(double resolution,double max_bucket_start)74 grpc_histogram* grpc_histogram_create(double resolution,
75                                       double max_bucket_start) {
76   grpc_histogram* h =
77       static_cast<grpc_histogram*>(gpr_malloc(sizeof(grpc_histogram)));
78   CHECK(resolution > 0.0);
79   CHECK(max_bucket_start > resolution);
80   h->sum = 0.0;
81   h->sum_of_squares = 0.0;
82   h->multiplier = 1.0 + resolution;
83   h->one_on_log_multiplier = 1.0 / log(1.0 + resolution);
84   h->max_possible = max_bucket_start;
85   h->count = 0.0;
86   h->min_seen = max_bucket_start;
87   h->max_seen = 0.0;
88   h->num_buckets = bucket_for_unchecked(h, max_bucket_start) + 1;
89   CHECK_GT(h->num_buckets, 1u);
90   CHECK_LT(h->num_buckets, 100000000ul);
91   h->buckets =
92       static_cast<uint32_t*>(gpr_zalloc(sizeof(uint32_t) * h->num_buckets));
93   return h;
94 }
95 
grpc_histogram_destroy(grpc_histogram * h)96 void grpc_histogram_destroy(grpc_histogram* h) {
97   gpr_free(h->buckets);
98   gpr_free(h);
99 }
100 
grpc_histogram_add(grpc_histogram * h,double x)101 void grpc_histogram_add(grpc_histogram* h, double x) {
102   h->sum += x;
103   h->sum_of_squares += x * x;
104   h->count++;
105   if (x < h->min_seen) {
106     h->min_seen = x;
107   }
108   if (x > h->max_seen) {
109     h->max_seen = x;
110   }
111   h->buckets[bucket_for(h, x)]++;
112 }
113 
grpc_histogram_merge(grpc_histogram * dst,const grpc_histogram * src)114 int grpc_histogram_merge(grpc_histogram* dst, const grpc_histogram* src) {
115   if ((dst->num_buckets != src->num_buckets) ||
116       (dst->multiplier != src->multiplier)) {
117     // Fail because these histograms don't match
118     return 0;
119   }
120   grpc_histogram_merge_contents(dst, src->buckets, src->num_buckets,
121                                 src->min_seen, src->max_seen, src->sum,
122                                 src->sum_of_squares, src->count);
123   return 1;
124 }
125 
grpc_histogram_merge_contents(grpc_histogram * histogram,const uint32_t * data,size_t data_count,double min_seen,double max_seen,double sum,double sum_of_squares,double count)126 void grpc_histogram_merge_contents(grpc_histogram* histogram,
127                                    const uint32_t* data, size_t data_count,
128                                    double min_seen, double max_seen, double sum,
129                                    double sum_of_squares, double count) {
130   size_t i;
131   CHECK(histogram->num_buckets == data_count);
132   histogram->sum += sum;
133   histogram->sum_of_squares += sum_of_squares;
134   histogram->count += count;
135   if (min_seen < histogram->min_seen) {
136     histogram->min_seen = min_seen;
137   }
138   if (max_seen > histogram->max_seen) {
139     histogram->max_seen = max_seen;
140   }
141   for (i = 0; i < histogram->num_buckets; i++) {
142     histogram->buckets[i] += data[i];
143   }
144 }
145 
threshold_for_count_below(grpc_histogram * h,double count_below)146 static double threshold_for_count_below(grpc_histogram* h, double count_below) {
147   double count_so_far;
148   double lower_bound;
149   double upper_bound;
150   size_t lower_idx;
151   size_t upper_idx;
152 
153   if (h->count == 0) {
154     return 0.0;
155   }
156 
157   if (count_below <= 0) {
158     return h->min_seen;
159   }
160   if (count_below >= h->count) {
161     return h->max_seen;
162   }
163 
164   // find the lowest bucket that gets us above count_below
165   count_so_far = 0.0;
166   for (lower_idx = 0; lower_idx < h->num_buckets; lower_idx++) {
167     count_so_far += h->buckets[lower_idx];
168     if (count_so_far >= count_below) {
169       break;
170     }
171   }
172   if (count_so_far == count_below) {
173     // this bucket hits the threshold exactly... we should be midway through
174     // any run of zero values following the bucket
175     for (upper_idx = lower_idx + 1; upper_idx < h->num_buckets; upper_idx++) {
176       if (h->buckets[upper_idx]) {
177         break;
178       }
179     }
180     return (bucket_start(h, static_cast<double>(lower_idx)) +
181             bucket_start(h, static_cast<double>(upper_idx))) /
182            2.0;
183   } else {
184     // treat values as uniform throughout the bucket, and find where this value
185     // should lie
186     lower_bound = bucket_start(h, static_cast<double>(lower_idx));
187     upper_bound = bucket_start(h, static_cast<double>(lower_idx + 1));
188     return grpc_core::Clamp(
189         upper_bound - ((upper_bound - lower_bound) *
190                        (count_so_far - count_below) / h->buckets[lower_idx]),
191         h->min_seen, h->max_seen);
192   }
193 }
194 
grpc_histogram_percentile(grpc_histogram * h,double percentile)195 double grpc_histogram_percentile(grpc_histogram* h, double percentile) {
196   return threshold_for_count_below(h, h->count * percentile / 100.0);
197 }
198 
grpc_histogram_mean(grpc_histogram * h)199 double grpc_histogram_mean(grpc_histogram* h) {
200   CHECK_NE(h->count, 0);
201   return h->sum / h->count;
202 }
203 
grpc_histogram_stddev(grpc_histogram * h)204 double grpc_histogram_stddev(grpc_histogram* h) {
205   return sqrt(grpc_histogram_variance(h));
206 }
207 
grpc_histogram_variance(grpc_histogram * h)208 double grpc_histogram_variance(grpc_histogram* h) {
209   if (h->count == 0) return 0.0;
210   return (h->sum_of_squares * h->count - h->sum * h->sum) /
211          (h->count * h->count);
212 }
213 
grpc_histogram_maximum(grpc_histogram * h)214 double grpc_histogram_maximum(grpc_histogram* h) { return h->max_seen; }
215 
grpc_histogram_minimum(grpc_histogram * h)216 double grpc_histogram_minimum(grpc_histogram* h) { return h->min_seen; }
217 
grpc_histogram_count(grpc_histogram * h)218 double grpc_histogram_count(grpc_histogram* h) { return h->count; }
219 
grpc_histogram_sum(grpc_histogram * h)220 double grpc_histogram_sum(grpc_histogram* h) { return h->sum; }
221 
grpc_histogram_sum_of_squares(grpc_histogram * h)222 double grpc_histogram_sum_of_squares(grpc_histogram* h) {
223   return h->sum_of_squares;
224 }
225 
grpc_histogram_get_contents(grpc_histogram * histogram,size_t * count)226 const uint32_t* grpc_histogram_get_contents(grpc_histogram* histogram,
227                                             size_t* count) {
228   *count = histogram->num_buckets;
229   return histogram->buckets;
230 }
231