HDRHistogram 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,415 @@
1
+ /**
2
+ * hdr_histogram.h
3
+ * Written by Michael Barker and released to the public domain,
4
+ * as explained at http://creativecommons.org/publicdomain/zero/1.0/
5
+ *
6
+ * The source for the hdr_histogram utilises a few C99 constructs, specifically
7
+ * the use of stdint/stdbool and inline variable declaration.
8
+ */
9
+
10
+ #ifndef HDR_HISTOGRAM_H
11
+ #define HDR_HISTOGRAM_H 1
12
+
13
+ #include <stdint.h>
14
+ #include <stdbool.h>
15
+ #include <stdio.h>
16
+
17
+ struct hdr_histogram
18
+ {
19
+ int64_t lowest_trackable_value;
20
+ int64_t highest_trackable_value;
21
+ int32_t unit_magnitude;
22
+ int32_t significant_figures;
23
+ int32_t sub_bucket_half_count_magnitude;
24
+ int32_t sub_bucket_half_count;
25
+ int64_t sub_bucket_mask;
26
+ int32_t sub_bucket_count;
27
+ int32_t bucket_count;
28
+ int64_t min_value;
29
+ int64_t max_value;
30
+ int32_t normalizing_index_offset;
31
+ double conversion_ratio;
32
+ int32_t counts_len;
33
+ int64_t total_count;
34
+ int64_t counts[0];
35
+ };
36
+
37
+ #ifdef __cplusplus
38
+ extern "C" {
39
+ #endif
40
+
41
+ /**
42
+ * Allocate the memory and initialise the hdr_histogram.
43
+ *
44
+ * Due to the size of the histogram being the result of some reasonably
45
+ * involved math on the input parameters this function it is tricky to stack allocate.
46
+ * The histogram is allocated in a single contigious block so can be delete via free,
47
+ * without any structure specific destructor.
48
+ *
49
+ * @param lowest_trackable_value The smallest possible value to be put into the
50
+ * histogram.
51
+ * @param highest_trackable_value The largest possible value to be put into the
52
+ * histogram.
53
+ * @param significant_figures The level of precision for this histogram, i.e. the number
54
+ * of figures in a decimal number that will be maintained. E.g. a value of 3 will mean
55
+ * the results from the histogram will be accurate up to the first three digits. Must
56
+ * be a value between 1 and 5 (inclusive).
57
+ * @param result Output parameter to capture allocated histogram.
58
+ * @return 0 on success, EINVAL if lowest_trackable_value is < 1 or the
59
+ * significant_figure value is outside of the allowed range, ENOMEM if malloc
60
+ * failed.
61
+ */
62
+ int hdr_init(
63
+ int64_t lowest_trackable_value,
64
+ int64_t highest_trackable_value,
65
+ int significant_figures,
66
+ struct hdr_histogram** result);
67
+
68
+ /**
69
+ * Allocate the memory and initialise the hdr_histogram. This is the equivalent of calling
70
+ * hdr_init(1, highest_trackable_value, significant_figures, result);
71
+ *
72
+ * @deprecated use hdr_init.
73
+ */
74
+ int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result);
75
+
76
+
77
+ /**
78
+ * Reset a histogram to zero - empty out a histogram and re-initialise it
79
+ *
80
+ * If you want to re-use an existing histogram, but reset everything back to zero, this
81
+ * is the routine to use.
82
+ *
83
+ * @param h The histogram you want to reset to empty.
84
+ *
85
+ */
86
+ void hdr_reset(struct hdr_histogram *h);
87
+
88
+ /**
89
+ * Get the memory size of the hdr_histogram.
90
+ *
91
+ * @param h "This" pointer
92
+ * @return The amount of memory used by the hdr_histogram in bytes
93
+ */
94
+ size_t hdr_get_memory_size(struct hdr_histogram *h);
95
+
96
+ /**
97
+ * Records a value in the histogram, will round this value of to a precision at or better
98
+ * than the significant_figure specified at construction time.
99
+ *
100
+ * @param h "This" pointer
101
+ * @param value Value to add to the histogram
102
+ * @return false if the value is larger than the highest_trackable_value and can't be recorded,
103
+ * true otherwise.
104
+ */
105
+ bool hdr_record_value(struct hdr_histogram* h, int64_t value);
106
+
107
+ /**
108
+ * Records count values in the histogram, will round this value of to a
109
+ * precision at or better than the significant_figure specified at construction
110
+ * time.
111
+ *
112
+ * @param h "This" pointer
113
+ * @param value Value to add to the histogram
114
+ * @param count Number of 'value's to add to the histogram
115
+ * @return false if any value is larger than the highest_trackable_value and can't be recorded,
116
+ * true otherwise.
117
+ */
118
+ bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count);
119
+
120
+
121
+ /**
122
+ * Record a value in the histogram and backfill based on an expected interval.
123
+ *
124
+ * Records a value in the histogram, will round this value of to a precision at or better
125
+ * than the significant_figure specified at contruction time. This is specifically used
126
+ * for recording latency. If the value is larger than the expected_interval then the
127
+ * latency recording system has experienced co-ordinated omission. This method fills in the
128
+ * values that would have occured had the client providing the load not been blocked.
129
+
130
+ * @param h "This" pointer
131
+ * @param value Value to add to the histogram
132
+ * @param expected_interval The delay between recording values.
133
+ * @return false if the value is larger than the highest_trackable_value and can't be recorded,
134
+ * true otherwise.
135
+ */
136
+ bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval);
137
+ /**
138
+ * Record a value in the histogram 'count' times. Applies the same correcting logic
139
+ * as 'hdr_record_corrected_value'.
140
+ *
141
+ * @param h "This" pointer
142
+ * @param value Value to add to the histogram
143
+ * @param count Number of 'value's to add to the histogram
144
+ * @param expected_interval The delay between recording values.
145
+ * @return false if the value is larger than the highest_trackable_value and can't be recorded,
146
+ * true otherwise.
147
+ */
148
+ bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval);
149
+
150
+ /**
151
+ * Adds all of the values from 'from' to 'this' histogram. Will return the
152
+ * number of values that are dropped when copying. Values will be dropped
153
+ * if they around outside of h.lowest_trackable_value and
154
+ * h.highest_trackable_value.
155
+ *
156
+ * @param h "This" pointer
157
+ * @param from Histogram to copy values from.
158
+ * @return The number of values dropped when copying.
159
+ */
160
+ int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from);
161
+
162
+ /**
163
+ * Adds all of the values from 'from' to 'this' histogram. Will return the
164
+ * number of values that are dropped when copying. Values will be dropped
165
+ * if they around outside of h.lowest_trackable_value and
166
+ * h.highest_trackable_value.
167
+ *
168
+ * @param h "This" pointer
169
+ * @param from Histogram to copy values from.
170
+ * @return The number of values dropped when copying.
171
+ */
172
+ int64_t hdr_add_while_correcting_for_coordinated_omission(
173
+ struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval);
174
+
175
+ /**
176
+ * Get minimum value from the histogram. Will return 2^63-1 if the histogram
177
+ * is empty.
178
+ *
179
+ * @param h "This" pointer
180
+ */
181
+ int64_t hdr_min(const struct hdr_histogram* h);
182
+
183
+ /**
184
+ * Get maximum value from the histogram. Will return 0 if the histogram
185
+ * is empty.
186
+ *
187
+ * @param h "This" pointer
188
+ */
189
+ int64_t hdr_max(const struct hdr_histogram* h);
190
+
191
+ /**
192
+ * Get the value at a specific percentile.
193
+ *
194
+ * @param h "This" pointer.
195
+ * @param percentile The percentile to get the value for
196
+ */
197
+ int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile);
198
+
199
+ /**
200
+ * Gets the standard deviation for the values in the histogram.
201
+ *
202
+ * @param h "This" pointer
203
+ * @return The standard deviation
204
+ */
205
+ double hdr_stddev(const struct hdr_histogram* h);
206
+
207
+ /**
208
+ * Gets the mean for the values in the histogram.
209
+ *
210
+ * @param h "This" pointer
211
+ * @return The mean
212
+ */
213
+ double hdr_mean(const struct hdr_histogram* h);
214
+
215
+ /**
216
+ * Determine if two values are equivalent with the histogram's resolution.
217
+ * Where "equivalent" means that value samples recorded for any two
218
+ * equivalent values are counted in a common total count.
219
+ *
220
+ * @param h "This" pointer
221
+ * @param a first value to compare
222
+ * @param b second value to compare
223
+ * @return 'true' if values are equivalent with the histogram's resolution.
224
+ */
225
+ bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b);
226
+
227
+ /**
228
+ * Get the lowest value that is equivalent to the given value within the histogram's resolution.
229
+ * Where "equivalent" means that value samples recorded for any two
230
+ * equivalent values are counted in a common total count.
231
+ *
232
+ * @param h "This" pointer
233
+ * @param value The given value
234
+ * @return The lowest value that is equivalent to the given value within the histogram's resolution.
235
+ */
236
+ int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value);
237
+
238
+ /**
239
+ * Get the count of recorded values at a specific value
240
+ * (to within the histogram resolution at the value level).
241
+ *
242
+ * @param h "This" pointer
243
+ * @param value The value for which to provide the recorded count
244
+ * @return The total count of values recorded in the histogram within the value range that is
245
+ * {@literal >=} lowestEquivalentValue(<i>value</i>) and {@literal <=} highestEquivalentValue(<i>value</i>)
246
+ */
247
+ int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value);
248
+ int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index);
249
+ int64_t hdr_value_at_index(const struct hdr_histogram* h, int32_t index);
250
+
251
+ struct hdr_iter_percentiles
252
+ {
253
+ bool seen_last_value;
254
+ int32_t ticks_per_half_distance;
255
+ double percentile_to_iterate_to;
256
+ double percentile;
257
+ };
258
+
259
+ struct hdr_iter_recorded
260
+ {
261
+ int64_t count_added_in_this_iteration_step;
262
+ };
263
+
264
+ struct hdr_iter_linear
265
+ {
266
+ int64_t value_units_per_bucket;
267
+ int64_t count_added_in_this_iteration_step;
268
+ int64_t next_value_reporting_level;
269
+ int64_t next_value_reporting_level_lowest_equivalent;
270
+ };
271
+
272
+ struct hdr_iter_log
273
+ {
274
+ double log_base;
275
+ int64_t count_added_in_this_iteration_step;
276
+ int64_t next_value_reporting_level;
277
+ int64_t next_value_reporting_level_lowest_equivalent;
278
+ };
279
+
280
+ /**
281
+ * The basic iterator. This is a generic structure
282
+ * that supports all of the types of iteration. Use
283
+ * the appropriate initialiser to get the desired
284
+ * iteration.
285
+ *
286
+ * @
287
+ */
288
+ struct hdr_iter
289
+ {
290
+ const struct hdr_histogram* h;
291
+ /** raw index into the counts array */
292
+ int32_t counts_index;
293
+ /** value directly from array for the current counts_index */
294
+ int64_t count;
295
+ /** sum of all of the counts up to and including the count at this index */
296
+ int64_t cumulative_count;
297
+ /** The current value based on counts_index */
298
+ int64_t value;
299
+ int64_t highest_equivalent_value;
300
+ int64_t lowest_equivalent_value;
301
+ int64_t median_equivalent_value;
302
+ int64_t value_iterated_from;
303
+ int64_t value_iterated_to;
304
+
305
+ union
306
+ {
307
+ struct hdr_iter_percentiles percentiles;
308
+ struct hdr_iter_recorded recorded;
309
+ struct hdr_iter_linear linear;
310
+ struct hdr_iter_log log;
311
+ } specifics;
312
+
313
+ bool (*_next_fp)(struct hdr_iter* iter);
314
+
315
+ };
316
+
317
+ /**
318
+ * Initalises the basic iterator.
319
+ *
320
+ * @param itr 'This' pointer
321
+ * @param h The histogram to iterate over
322
+ */
323
+ void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h);
324
+
325
+ /**
326
+ * Initialise the iterator for use with percentiles.
327
+ */
328
+ void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance);
329
+
330
+ /**
331
+ * Initialise the iterator for use with recorded values.
332
+ */
333
+ void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h);
334
+
335
+ /**
336
+ * Initialise the iterator for use with linear values.
337
+ */
338
+ void hdr_iter_linear_init(
339
+ struct hdr_iter* iter,
340
+ const struct hdr_histogram* h,
341
+ int64_t value_units_per_bucket);
342
+
343
+ /**
344
+ * Initialise the iterator for use with logarithmic values
345
+ */
346
+ void hdr_iter_log_init(
347
+ struct hdr_iter* iter,
348
+ const struct hdr_histogram* h,
349
+ int64_t value_units_first_bucket,
350
+ double log_base);
351
+
352
+ /**
353
+ * Iterate to the next value for the iterator. If there are no more values
354
+ * available return faluse.
355
+ *
356
+ * @param itr 'This' pointer
357
+ * @return 'false' if there are no values remaining for this iterator.
358
+ */
359
+ bool hdr_iter_next(struct hdr_iter* iter);
360
+
361
+ typedef enum {
362
+ CLASSIC,
363
+ CSV
364
+ } format_type;
365
+
366
+ /**
367
+ * Print out a percentile based histogram to the supplied stream. Note that
368
+ * this call will not flush the FILE, this is left up to the user.
369
+ *
370
+ * @param h 'This' pointer
371
+ * @param stream The FILE to write the output to
372
+ * @param ticks_per_half_distance The number of iteration steps per half-distance to 100%
373
+ * @param value_scale Scale the output values by this amount
374
+ * @param format_type Format to use, e.g. CSV.
375
+ * @return 0 on success, error code on failure. EIO if an error occurs writing
376
+ * the output.
377
+ */
378
+ int hdr_percentiles_print(
379
+ struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance,
380
+ double value_scale, format_type format);
381
+
382
+ /**
383
+ * Internal allocation methods, used by hdr_dbl_histogram.
384
+ */
385
+ struct hdr_histogram_bucket_config
386
+ {
387
+ int64_t lowest_trackable_value;
388
+ int64_t highest_trackable_value;
389
+ int64_t unit_magnitude;
390
+ int64_t significant_figures;
391
+ int32_t sub_bucket_half_count_magnitude;
392
+ int32_t sub_bucket_half_count;
393
+ int64_t sub_bucket_mask;
394
+ int32_t sub_bucket_count;
395
+ int32_t bucket_count;
396
+ int32_t counts_len;
397
+ };
398
+
399
+ int hdr_calculate_bucket_config(
400
+ int64_t lowest_trackable_value,
401
+ int64_t highest_trackable_value,
402
+ int significant_figures,
403
+ struct hdr_histogram_bucket_config* cfg);
404
+
405
+ void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg);
406
+
407
+ int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram *h, int64_t value);
408
+ int64_t hdr_next_non_equivalent_value(const struct hdr_histogram *h, int64_t value);
409
+ int64_t hdr_median_equivalent_value(const struct hdr_histogram *h, int64_t value);
410
+
411
+ #ifdef __cplusplus
412
+ }
413
+ #endif
414
+
415
+ #endif
@@ -0,0 +1,152 @@
1
+ #include "ruby.h"
2
+ //#include "ext_help.h"
3
+ #include <errno.h>
4
+ #include <inttypes.h>
5
+ #include "hdr_histogram.h"
6
+
7
+ #define GET_HDRHIST(name, val) \
8
+ struct hdr_histogram *name; \
9
+ Data_Get_Struct(val, struct hdr_histogram, name)
10
+
11
+ static VALUE HDRHistogram = Qnil;
12
+ static VALUE HDRHistogramError = Qnil;
13
+
14
+ static void histogram_free(void *p) {
15
+ free(p);
16
+ }
17
+
18
+ static VALUE histogram_new(int argc, VALUE* argv, VALUE class) {
19
+ VALUE self, lowest_value, highest_value, significant_figures;
20
+ VALUE opt;
21
+
22
+ struct hdr_histogram *hdrh;
23
+ int ret;
24
+
25
+ rb_scan_args(argc, argv, "31", &lowest_value, &highest_value, &significant_figures, &opt);
26
+
27
+ lowest_value = rb_funcall(class, rb_intern("adjusted_boundary_val"), 2, lowest_value, opt);
28
+ highest_value = rb_funcall(class, rb_intern("adjusted_boundary_val"), 2, highest_value, opt);
29
+
30
+ ret = hdr_init(NUM2INT(lowest_value), NUM2INT(highest_value), NUM2INT(significant_figures), &hdrh);
31
+ if(ret == EINVAL) {
32
+ rb_raise(HDRHistogramError, "%s", "lowest_trackable_value must be >= 1");
33
+ }
34
+ else if(ret == ENOMEM) {
35
+ rb_raise(HDRHistogramError, "%s", "no memory");
36
+ }
37
+
38
+ self = Data_Wrap_Struct(class, NULL, histogram_free, hdrh);
39
+ rb_obj_call_init(self, argc, argv);
40
+
41
+ return self;
42
+ }
43
+
44
+ static VALUE histogram_reset(VALUE self) {
45
+ GET_HDRHIST(hdr, self);
46
+ hdr_reset(hdr);
47
+ return self;
48
+ }
49
+
50
+ static VALUE histogram_memsize(VALUE self) {
51
+ GET_HDRHIST(hdr, self);
52
+ return INT2NUM(hdr_get_memory_size(hdr));
53
+ }
54
+
55
+ static VALUE histogram_count(VALUE self) {
56
+ GET_HDRHIST(hdr, self);
57
+ return INT2NUM(hdr->total_count);
58
+ }
59
+
60
+ static VALUE histogram_record_value(VALUE self, VALUE val) {
61
+ GET_HDRHIST(hdr, self);
62
+ return hdr_record_value(hdr, NUM2INT(val)) ? Qtrue : Qfalse;
63
+ }
64
+
65
+ static VALUE histogram_record_corrected_value(VALUE self, VALUE val, VALUE expected_interval) {
66
+ GET_HDRHIST(hdr, self);
67
+ return hdr_record_corrected_value(hdr, NUM2INT(val), NUM2INT(expected_interval)) ? Qtrue : Qfalse;
68
+ }
69
+
70
+ static VALUE generic_histogram_intval(VALUE self, int64_t func(const struct hdr_histogram *) ) {
71
+ GET_HDRHIST(hdr, self);
72
+ return INT2NUM(hdr->total_count > 0 ? func(hdr) : 0);
73
+ }
74
+
75
+ static VALUE histogram_min(VALUE self) {
76
+ return generic_histogram_intval(self, hdr_min);
77
+ }
78
+ static VALUE histogram_max(VALUE self) {
79
+ return generic_histogram_intval(self, hdr_max);
80
+ }
81
+
82
+ static VALUE generic_histogram_floatval(VALUE self, double func(const struct hdr_histogram *) ) {
83
+ GET_HDRHIST(hdr, self);
84
+ return rb_float_new(hdr->total_count > 0 ? func(hdr) : 0.0);
85
+ }
86
+
87
+ static VALUE histogram_mean(VALUE self) {
88
+ return generic_histogram_floatval(self, hdr_mean);
89
+ }
90
+
91
+ static VALUE histogram_stddev(VALUE self) {
92
+ return generic_histogram_floatval(self, hdr_stddev);
93
+ }
94
+
95
+ static VALUE histogram_percentile(VALUE self, VALUE percentile ) {
96
+ GET_HDRHIST(hdr, self);
97
+ return INT2NUM(hdr_value_at_percentile(hdr, NUM2DBL(percentile)));
98
+ }
99
+
100
+ static VALUE histogram_merge(VALUE self, VALUE another ) {
101
+ GET_HDRHIST(hdr, self);
102
+ GET_HDRHIST(hdr2, another);
103
+ return INT2NUM(hdr_add(hdr, hdr2));
104
+ }
105
+
106
+ #define HISTOGRAM_GETINT_METHOD(int_name) \
107
+ static VALUE histogram_##int_name(VALUE self) { \
108
+ GET_HDRHIST(hdr, self); \
109
+ return INT2NUM(hdr->int_name); \
110
+ }
111
+
112
+ HISTOGRAM_GETINT_METHOD(lowest_trackable_value)
113
+ HISTOGRAM_GETINT_METHOD(highest_trackable_value)
114
+ HISTOGRAM_GETINT_METHOD(unit_magnitude)
115
+ HISTOGRAM_GETINT_METHOD(significant_figures)
116
+ HISTOGRAM_GETINT_METHOD(bucket_count)
117
+ HISTOGRAM_GETINT_METHOD(sub_bucket_count)
118
+ HISTOGRAM_GETINT_METHOD(counts_len)
119
+
120
+ void Init_ruby_hdr_histogram() {
121
+ HDRHistogram = rb_define_class("HDRHistogram", rb_cObject);
122
+ HDRHistogramError = rb_define_class_under(HDRHistogram, "HDRHistogramError", rb_eRuntimeError);
123
+
124
+ rb_define_singleton_method(HDRHistogram, "new", histogram_new, -1);
125
+ rb_define_attr(HDRHistogram, "multiplier", 1, 0);
126
+ rb_define_attr(HDRHistogram, "unit", 1, 0);
127
+
128
+ rb_define_method(HDRHistogram, "reset", histogram_reset, 0);
129
+ rb_define_method(HDRHistogram, "memsize", histogram_memsize, 0);
130
+ rb_define_method(HDRHistogram, "count", histogram_count, 0);
131
+
132
+ rb_define_private_method(HDRHistogram, "raw_record", histogram_record_value, 1);
133
+ rb_define_private_method(HDRHistogram, "raw_record_corrected", histogram_record_corrected_value, 2);
134
+ rb_define_private_method(HDRHistogram, "raw_min", histogram_min, 0);
135
+ rb_define_private_method(HDRHistogram, "raw_max", histogram_max, 0);
136
+ rb_define_private_method(HDRHistogram, "raw_mean", histogram_mean, 0);
137
+ rb_define_private_method(HDRHistogram, "raw_stddev", histogram_stddev, 0);
138
+ rb_define_private_method(HDRHistogram, "raw_percentile", histogram_percentile, 1);
139
+ rb_define_private_method(HDRHistogram, "raw_merge", histogram_merge, 1);
140
+
141
+
142
+ rb_define_method(HDRHistogram, "lowest_trackable_value", histogram_lowest_trackable_value, 0);
143
+ rb_define_method(HDRHistogram, "highest_trackable_value", histogram_highest_trackable_value, 0);
144
+ rb_define_private_method(HDRHistogram, "unit_magnitude", histogram_unit_magnitude, 0);
145
+ rb_define_method(HDRHistogram, "significant_figures", histogram_significant_figures, 0);
146
+ rb_define_private_method(HDRHistogram, "bucket_count", histogram_bucket_count, 0);
147
+ rb_define_private_method(HDRHistogram, "sub_bucket_count", histogram_bucket_count, 0);
148
+ rb_define_private_method(HDRHistogram, "counts_len", histogram_counts_len, 0);
149
+
150
+ //rb_define_private_method(HDRHistogram, "histogram_spectrum", histogram_spectrum, 2);
151
+ }
152
+