Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: third_party/grpc/src/core/statistics/window_stats.c

Issue 1932353002: Initial checkin of gRPC to third_party/ Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 *
3 * Copyright 2015, Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34 #include "src/core/statistics/window_stats.h"
35 #include <math.h>
36 #include <stddef.h>
37 #include <string.h>
38 #include <grpc/support/alloc.h>
39 #include <grpc/support/log.h>
40 #include <grpc/support/time.h>
41 #include <grpc/support/useful.h>
42
43 /* typedefs make typing long names easier. Use cws (for census_window_stats) */
44 typedef census_window_stats_stat_info cws_stat_info;
45 typedef struct census_window_stats_sum cws_sum;
46
47 /* Each interval is composed of a number of buckets, which hold a count of
48 entries and a single statistic */
49 typedef struct census_window_stats_bucket {
50 int64_t count;
51 void *statistic;
52 } cws_bucket;
53
54 /* Each interval has a set of buckets, and the variables needed to keep
55 track of their current state */
56 typedef struct census_window_stats_interval_stats {
57 /* The buckets. There will be 'granularity' + 1 of these. */
58 cws_bucket *buckets;
59 /* Index of the bucket containing the smallest time interval. */
60 int bottom_bucket;
61 /* The smallest time storable in the current window. */
62 int64_t bottom;
63 /* The largest time storable in the current window + 1ns */
64 int64_t top;
65 /* The width of each bucket in ns. */
66 int64_t width;
67 } cws_interval_stats;
68
69 typedef struct census_window_stats {
70 /* Number of intervals. */
71 int nintervals;
72 /* Number of buckets in each interval. 'granularity' + 1. */
73 int nbuckets;
74 /* Record of stat_info. */
75 cws_stat_info stat_info;
76 /* Stats for each interval. */
77 cws_interval_stats *interval_stats;
78 /* The time the newset stat was recorded. */
79 int64_t newest_time;
80 } window_stats;
81
82 /* Calculate an actual bucket index from a logical index 'IDX'. Other
83 parameters supply information on the interval struct and overall stats. */
84 #define BUCKET_IDX(IS, IDX, WSTATS) \
85 ((IS->bottom_bucket + (IDX)) % WSTATS->nbuckets)
86
87 /* The maximum seconds value we can have in a valid timespec. More than this
88 will result in overflow in timespec_to_ns(). This works out to ~292 years.
89 TODO: consider using doubles instead of int64. */
90 static int64_t max_seconds = (GPR_INT64_MAX - GPR_NS_PER_SEC) / GPR_NS_PER_SEC;
91
92 static int64_t timespec_to_ns(const gpr_timespec ts) {
93 if (ts.tv_sec > max_seconds) {
94 return GPR_INT64_MAX - 1;
95 }
96 return ts.tv_sec * GPR_NS_PER_SEC + ts.tv_nsec;
97 }
98
99 static void cws_initialize_statistic(void *statistic,
100 const cws_stat_info *stat_info) {
101 if (stat_info->stat_initialize == NULL) {
102 memset(statistic, 0, stat_info->stat_size);
103 } else {
104 stat_info->stat_initialize(statistic);
105 }
106 }
107
108 /* Create and initialize a statistic */
109 static void *cws_create_statistic(const cws_stat_info *stat_info) {
110 void *stat = gpr_malloc(stat_info->stat_size);
111 cws_initialize_statistic(stat, stat_info);
112 return stat;
113 }
114
115 window_stats *census_window_stats_create(int nintervals,
116 const gpr_timespec intervals[],
117 int granularity,
118 const cws_stat_info *stat_info) {
119 window_stats *ret;
120 int i;
121 /* validate inputs */
122 GPR_ASSERT(nintervals > 0 && granularity > 2 && intervals != NULL &&
123 stat_info != NULL);
124 for (i = 0; i < nintervals; i++) {
125 int64_t ns = timespec_to_ns(intervals[i]);
126 GPR_ASSERT(intervals[i].tv_sec >= 0 && intervals[i].tv_nsec >= 0 &&
127 intervals[i].tv_nsec < GPR_NS_PER_SEC && ns >= 100 &&
128 granularity * 10 <= ns);
129 }
130 /* Allocate and initialize relevant data structures */
131 ret = (window_stats *)gpr_malloc(sizeof(window_stats));
132 ret->nintervals = nintervals;
133 ret->nbuckets = granularity + 1;
134 ret->stat_info = *stat_info;
135 ret->interval_stats =
136 (cws_interval_stats *)gpr_malloc(nintervals * sizeof(cws_interval_stats));
137 for (i = 0; i < nintervals; i++) {
138 int64_t size_ns = timespec_to_ns(intervals[i]);
139 cws_interval_stats *is = ret->interval_stats + i;
140 cws_bucket *buckets = is->buckets =
141 (cws_bucket *)gpr_malloc(ret->nbuckets * sizeof(cws_bucket));
142 int b;
143 for (b = 0; b < ret->nbuckets; b++) {
144 buckets[b].statistic = cws_create_statistic(stat_info);
145 buckets[b].count = 0;
146 }
147 is->bottom_bucket = 0;
148 is->bottom = 0;
149 is->width = size_ns / granularity;
150 /* Check for possible overflow issues, and maximize interval size if the
151 user requested something large enough. */
152 if ((GPR_INT64_MAX - is->width) > size_ns) {
153 is->top = size_ns + is->width;
154 } else {
155 is->top = GPR_INT64_MAX;
156 is->width = GPR_INT64_MAX / (granularity + 1);
157 }
158 /* If size doesn't divide evenly, we can have a width slightly too small;
159 better to have it slightly large. */
160 if ((size_ns - (granularity + 1) * is->width) > 0) {
161 is->width += 1;
162 }
163 }
164 ret->newest_time = 0;
165 return ret;
166 }
167
168 /* When we try adding a measurement above the current interval range, we
169 need to "shift" the buckets sufficiently to cover the new range. */
170 static void cws_shift_buckets(const window_stats *wstats,
171 cws_interval_stats *is, int64_t when_ns) {
172 int i;
173 /* number of bucket time widths to "shift" */
174 int shift;
175 /* number of buckets to clear */
176 int nclear;
177 GPR_ASSERT(when_ns >= is->top);
178 /* number of bucket time widths to "shift" */
179 shift = ((when_ns - is->top) / is->width) + 1;
180 /* number of buckets to clear - limited by actual number of buckets */
181 nclear = GPR_MIN(shift, wstats->nbuckets);
182 for (i = 0; i < nclear; i++) {
183 int b = BUCKET_IDX(is, i, wstats);
184 is->buckets[b].count = 0;
185 cws_initialize_statistic(is->buckets[b].statistic, &wstats->stat_info);
186 }
187 /* adjust top/bottom times and current bottom bucket */
188 is->bottom_bucket = BUCKET_IDX(is, shift, wstats);
189 is->top += shift * is->width;
190 is->bottom += shift * is->width;
191 }
192
193 void census_window_stats_add(window_stats *wstats, const gpr_timespec when,
194 const void *stat_value) {
195 int i;
196 int64_t when_ns = timespec_to_ns(when);
197 GPR_ASSERT(wstats->interval_stats != NULL);
198 for (i = 0; i < wstats->nintervals; i++) {
199 cws_interval_stats *is = wstats->interval_stats + i;
200 cws_bucket *bucket;
201 if (when_ns < is->bottom) { /* Below smallest time in interval: drop */
202 continue;
203 }
204 if (when_ns >= is->top) { /* above limit: shift buckets */
205 cws_shift_buckets(wstats, is, when_ns);
206 }
207 /* Add the stat. */
208 GPR_ASSERT(is->bottom <= when_ns && when_ns < is->top);
209 bucket = is->buckets +
210 BUCKET_IDX(is, (when_ns - is->bottom) / is->width, wstats);
211 bucket->count++;
212 wstats->stat_info.stat_add(bucket->statistic, stat_value);
213 }
214 if (when_ns > wstats->newest_time) {
215 wstats->newest_time = when_ns;
216 }
217 }
218
219 /* Add a specific bucket contents to an accumulating total. */
220 static void cws_add_bucket_to_sum(cws_sum *sum, const cws_bucket *bucket,
221 const cws_stat_info *stat_info) {
222 sum->count += bucket->count;
223 stat_info->stat_add(sum->statistic, bucket->statistic);
224 }
225
226 /* Add a proportion to an accumulating sum. */
227 static void cws_add_proportion_to_sum(double p, cws_sum *sum,
228 const cws_bucket *bucket,
229 const cws_stat_info *stat_info) {
230 sum->count += p * bucket->count;
231 stat_info->stat_add_proportion(p, sum->statistic, bucket->statistic);
232 }
233
234 void census_window_stats_get_sums(const window_stats *wstats,
235 const gpr_timespec when, cws_sum sums[]) {
236 int i;
237 int64_t when_ns = timespec_to_ns(when);
238 GPR_ASSERT(wstats->interval_stats != NULL);
239 for (i = 0; i < wstats->nintervals; i++) {
240 int when_bucket;
241 int new_bucket;
242 double last_proportion = 1.0;
243 double bottom_proportion;
244 cws_interval_stats *is = wstats->interval_stats + i;
245 cws_sum *sum = sums + i;
246 sum->count = 0;
247 cws_initialize_statistic(sum->statistic, &wstats->stat_info);
248 if (when_ns < is->bottom) {
249 continue;
250 }
251 if (when_ns >= is->top) {
252 cws_shift_buckets(wstats, is, when_ns);
253 }
254 /* Calculating the appropriate amount of which buckets to use can get
255 complicated. Essentially there are two cases:
256 1) if the "top" bucket (new_bucket, where the newest additions to the
257 stats recorded are entered) corresponds to 'when', then we need
258 to take a proportion of it - (if when < newest_time) or the full
259 thing. We also (possibly) need to take a corresponding
260 proportion of the bottom bucket.
261 2) Other cases, we just take a straight proportion.
262 */
263 when_bucket = (when_ns - is->bottom) / is->width;
264 new_bucket = (wstats->newest_time - is->bottom) / is->width;
265 if (new_bucket == when_bucket) {
266 int64_t bottom_bucket_time = is->bottom + when_bucket * is->width;
267 if (when_ns < wstats->newest_time) {
268 last_proportion = (double)(when_ns - bottom_bucket_time) /
269 (double)(wstats->newest_time - bottom_bucket_time);
270 bottom_proportion =
271 (double)(is->width - (when_ns - bottom_bucket_time)) / is->width;
272 } else {
273 bottom_proportion =
274 (double)(is->width - (wstats->newest_time - bottom_bucket_time)) /
275 is->width;
276 }
277 } else {
278 last_proportion =
279 (double)(when_ns + 1 - is->bottom - when_bucket * is->width) /
280 is->width;
281 bottom_proportion = 1.0 - last_proportion;
282 }
283 cws_add_proportion_to_sum(last_proportion, sum,
284 is->buckets + BUCKET_IDX(is, when_bucket, wstats),
285 &wstats->stat_info);
286 if (when_bucket != 0) { /* last bucket isn't also bottom bucket */
287 int b;
288 /* Add all of "bottom" bucket if we are looking at a subset of the
289 full interval, or a proportion if we are adding full interval. */
290 cws_add_proportion_to_sum(
291 (when_bucket == wstats->nbuckets - 1 ? bottom_proportion : 1.0), sum,
292 is->buckets + is->bottom_bucket, &wstats->stat_info);
293 /* Add all the remaining buckets (everything but top and bottom). */
294 for (b = 1; b < when_bucket; b++) {
295 cws_add_bucket_to_sum(sum, is->buckets + BUCKET_IDX(is, b, wstats),
296 &wstats->stat_info);
297 }
298 }
299 }
300 }
301
302 void census_window_stats_destroy(window_stats *wstats) {
303 int i;
304 GPR_ASSERT(wstats->interval_stats != NULL);
305 for (i = 0; i < wstats->nintervals; i++) {
306 int b;
307 for (b = 0; b < wstats->nbuckets; b++) {
308 gpr_free(wstats->interval_stats[i].buckets[b].statistic);
309 }
310 gpr_free(wstats->interval_stats[i].buckets);
311 }
312 gpr_free(wstats->interval_stats);
313 /* Ensure any use-after free triggers assert. */
314 wstats->interval_stats = NULL;
315 gpr_free(wstats);
316 }
OLDNEW
« no previous file with comments | « third_party/grpc/src/core/statistics/window_stats.h ('k') | third_party/grpc/src/core/support/alloc.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698