HDRHistogram 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+