HDRHistogram 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 86b83bac63e24d969875428fce79d9a97261d568
4
+ data.tar.gz: 195a4703ddab5086b0c878bf6c8ea556ace8a899
5
+ SHA512:
6
+ metadata.gz: bf9ab3e2fd4b062d067a69f2b41bdb9cef903d48703a12b3c7a39ec9c541e5df4707b3fa01b613e465e86a21b038bb0e3cd5e14027557343ddafab855b30e91a
7
+ data.tar.gz: 5b58198b8c02e0aae13b537ec6e0c4d00f1bb6ca5bd1bd3a2a0d450903ce408cc68e64366ec78baedbf84b32124617e8a0b9a1aadd7dbeb321071378a2f6af91
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /lib/*.so
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.1
4
+ before_install: gem install bundler -v 1.11.2
@@ -0,0 +1,7 @@
1
+ In the spirit of inclusiveness, cooperation and community, this software is
2
+ distributed with the strictly enforced CYHTFYW Code of Conduct. Failure to
3
+ comply with this code may result in punitive action and loss of privilege.
4
+
5
+ ## CYHTFYW Code of Conduct:
6
+
7
+ 1. Conduct yourself however the fuck you want.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in HDRHistogram.gemspec
4
+ gemspec
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'HDRHistogram/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "HDRHistogram"
8
+ spec.version = HDRHistogram::VERSION
9
+ spec.authors = ["Leo P."]
10
+ spec.email = ["junk@slact.net"]
11
+
12
+ spec.summary = "Ruby wrapper for the C hdr_histogram library"
13
+ spec.description = "HdrHistogram is an algorithm designed for recording histograms of value measurements with configurable precision. Value precision is expressed as the number of significant digits, providing control over value quantization and resolution whilst maintaining a fixed cost in both space and time."
14
+ spec.homepage = "https://github.com/slact/hdr_histogram_ruby"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.extensions = ["ext/ruby_hdr_histogram/extconf.rb"]
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_development_dependency "bundler", "~> 1.11"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "rake-compiler"
34
+ spec.add_development_dependency "minitest", "~> 5.0"
35
+ spec.add_development_dependency "pry"
36
+ spec.add_development_dependency "pry-debundle"
37
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Leo P.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,144 @@
1
+ #Ruby HdrHistogram Library
2
+
3
+ ## Overview
4
+ HdrHistogram is an algorithm designed for recording histograms of value measurements with configurable precision. Value precision is expressed as the number of significant digits, providing control over value quantization and resolution whilst maintaining a fixed cost in both space and time.
5
+ More information can be found on the [HdrHistogram site](http://hdrhistogram.org/) (which much of the text in this README paraphrases). This library wraps the [C port](https://github.com/HdrHistogram/HdrHistogram_c).
6
+
7
+
8
+ ## Installation
9
+
10
+ ```shell
11
+ gem install HDRHistogram
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ### Examples
17
+
18
+ #### Basic usage
19
+ ```ruby
20
+
21
+ require "HDRHistogram"
22
+
23
+ hdr = HDRHistogram.new(1,1000000,3)
24
+
25
+ i=10
26
+ while i != 1000000 do
27
+ hdr.record(i)
28
+ i+=10
29
+ end
30
+
31
+ p50 = hdr.percentile(50)
32
+ #p50 == 500223
33
+ ```
34
+
35
+ #### Multipliers and units
36
+ While hdr_histogram internally can represent only integers between 1 and an integer upper bound, the Ruby HDRHistogram can be initialized with a multiplier to adjust the recorded values, as well as a named unit for the values for output:
37
+ ```ruby
38
+ #record milliseconds with 3 digits past the decimal
39
+ hdr = HDRHistogram.new(0.001,1000, 3, multiplier: 0.001, unit: :ms)
40
+
41
+ i=0.001
42
+ while i <= 1000 do
43
+ hdr.record(i)
44
+ i += 0.010
45
+ end
46
+
47
+ puts hdr.stats
48
+ # 10.000% 100.031ms
49
+ # 20.000% 200.063ms
50
+ # 30.000% 300.031ms
51
+ # 40.000% 400.127ms
52
+ # 50.000% 500.223ms
53
+ # 60.000% 600.063ms
54
+ # 70.000% 700.415ms
55
+ # 80.000% 800.255ms
56
+ # 90.000% 900.095ms
57
+ # 100.000% 1000.447ms
58
+
59
+ puts hdr.latency_stats
60
+ # Latency Stats
61
+ # 50.000% 500.223ms
62
+ # 75.000% 750.079ms
63
+ # 90.000% 900.095ms
64
+ # 99.000% 990.207ms
65
+ # 99.900% 999.423ms
66
+ # 99.990% 999.935ms
67
+ # 99.999% 1000.447ms
68
+ # 100.000% 1000.447ms
69
+ ```
70
+
71
+ ## API
72
+
73
+ #### `hdr = HDRHistogram.new(lowest_value, highest_value, significant_figures, multiplier: 1, unit: nil)`
74
+ Create new HDRHistogram object.
75
+ - `lowest_value`: The smallest possible value to be put into the histogram.
76
+ - `highest_value`: The largest possible value to be put into the histogram.
77
+ - `significant_figures`: The level of precision for this histogram, i.e. the number of figures in a decimal number that will be maintained. E.g. a value of 3 will mean the results from the histogram will be accurate up to the first three digits. Must be a value between 1 and 5 (inclusive).
78
+ - `:multiplier`: A multiplier to adjust all incoming values. If present, the raw value recorded in the histogram for `record(val)` will be `val * 1/multiplier`. Similarly, `percentile(pctl) => val * multiplier`. If `multiplier` < 1, `lowest_value` can be < 1 so that `lowest_value` * 1/`multiplier` == 1.
79
+ - `:unit`: A unit for labeling histogram values. Useful for outputting things.
80
+
81
+
82
+
83
+ #### `hdr.record(value)`
84
+ Records a `value` in the histogram, will round this `value` of to a precision at or better than the `significant_figures` specified at construction time.
85
+
86
+ #### `hdr.record_corrected(value, expected_interval)`
87
+ Record a `value` in the histogram and backfill based on an expected interval.
88
+
89
+ This is specifically used for recording latency. If the `value` is larger than the `expected_interval` then the latency recording system has experienced [co-ordinated omission](https://github.com/giltene/wrk2#acknowledgements). This method fills in the values that would have occured had the client providing the load not been blocked.
90
+
91
+ #### `hdr.percentile(pct)`
92
+ Get the value at a specific percentile.
93
+
94
+ #### `hdr.count`
95
+ Get the total number of recorded values in the histogram.
96
+
97
+ #### `hdr.min`
98
+ Get minimum value from the histogram. Will return 0 if the histogram is empty.
99
+
100
+ #### `hdr.max`
101
+ Get maximum value from the histogram. Will return 0 if the histogram is empty.
102
+
103
+ #### `hdr.stddev`
104
+ Get the standard deviation for the values in the histogram.
105
+
106
+ #### `hdr.mean`
107
+ Get the mean (average) for the values in the histogram.
108
+
109
+ #### `hdr.reset`
110
+ Reset a histogram to zero - empty out a histogram and re-initialise it. If you want to re-use an existing histogram, but reset everything back to zero, this is the method to use.
111
+
112
+ #### `hdr.memsize`
113
+ Get the memory size (in bytes) of the histogram data.
114
+
115
+ #### `hdr.stats(percentiles=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100])`
116
+ Get a formatted string with percentile stats of the histogram:
117
+ ```
118
+ # 10.000% 100
119
+ # 20.000% 200
120
+ # 30.000% 300
121
+ # 40.000% 400
122
+ # 50.000% 500
123
+ # 60.000% 600
124
+ # 70.000% 700
125
+ # 80.000% 800
126
+ # 90.000% 900
127
+ # 100.000% 1000
128
+ ```
129
+ If the histogram was initialized with a `unit`, it will be shown after each percentile value.
130
+
131
+ #### `hdr.latency_stats`
132
+ Get a formatted string with percentile stats of the histogram useful for latency measurement:
133
+ ```
134
+ # Latency Stats
135
+ # 50.000% 500.223ms
136
+ # 75.000% 750.079ms
137
+ # 90.000% 900.095ms
138
+ # 99.000% 990.207ms
139
+ # 99.900% 999.423ms
140
+ # 99.990% 999.935ms
141
+ # 99.999% 1000.447ms
142
+ # 100.000% 1000.447ms
143
+ ```
144
+ The above output assumes a :multiplier of 0.001 and a :unit of `:ms`
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/extensiontask"
3
+ task :default => :spec
4
+ Rake::ExtensionTask.new 'ruby_hdr_histogram'
5
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "HDRHistogram"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
12
+
13
+ #require "irb"
14
+ #IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ require "mkmf"
2
+
3
+ extension_name = "ruby_hdr_histogram"
4
+
5
+ create_header
6
+ create_makefile extension_name
@@ -0,0 +1,1008 @@
1
+ /**
2
+ * hdr_histogram.c
3
+ * Written by Michael Barker and released to the public domain,
4
+ * as explained at http://creativecommons.org/publicdomain/zero/1.0/
5
+ */
6
+
7
+ #include <stdlib.h>
8
+ #include <stdbool.h>
9
+ #include <math.h>
10
+ #include <assert.h>
11
+ #include <stdio.h>
12
+ #include <string.h>
13
+ #include <stdint.h>
14
+ #include <errno.h>
15
+ #include <inttypes.h>
16
+
17
+ #include "hdr_histogram.h"
18
+
19
+ // ###### ####### ## ## ## ## ######## ######
20
+ // ## ## ## ## ## ## ### ## ## ## ##
21
+ // ## ## ## ## ## #### ## ## ##
22
+ // ## ## ## ## ## ## ## ## ## ######
23
+ // ## ## ## ## ## ## #### ## ##
24
+ // ## ## ## ## ## ## ## ### ## ## ##
25
+ // ###### ####### ####### ## ## ## ######
26
+
27
+ static int32_t normalize_index(const struct hdr_histogram* h, int32_t index)
28
+ {
29
+ if (h->normalizing_index_offset == 0)
30
+ {
31
+ return index;
32
+ }
33
+
34
+ int32_t normalized_index = index - h->normalizing_index_offset;
35
+ int32_t adjustment = 0;
36
+
37
+ if (normalized_index < 0)
38
+ {
39
+ adjustment = h->counts_len;
40
+ }
41
+ else if (normalized_index >= h->counts_len)
42
+ {
43
+ adjustment = -h->counts_len;
44
+ }
45
+
46
+ return normalized_index + adjustment;
47
+ }
48
+
49
+ static int64_t counts_get_direct(const struct hdr_histogram* h, int32_t index)
50
+ {
51
+ return h->counts[index];
52
+ }
53
+
54
+ static int64_t counts_get_normalised(const struct hdr_histogram* h, int32_t index)
55
+ {
56
+ return counts_get_direct(h, normalize_index(h, index));
57
+ }
58
+
59
+ static void counts_inc_normalised(
60
+ struct hdr_histogram* h, int32_t index, int64_t value)
61
+ {
62
+ int32_t normalised_index = normalize_index(h, index);
63
+ h->counts[normalised_index] += value;
64
+ h->total_count += value;
65
+ }
66
+
67
+ static void update_min_max(struct hdr_histogram* h, int64_t value)
68
+ {
69
+ h->min_value = (value < h->min_value && value != 0) ? value : h->min_value;
70
+ h->max_value = (value > h->max_value) ? value : h->max_value;
71
+ }
72
+
73
+ // ## ## ######## #### ## #### ######## ## ##
74
+ // ## ## ## ## ## ## ## ## ##
75
+ // ## ## ## ## ## ## ## ####
76
+ // ## ## ## ## ## ## ## ##
77
+ // ## ## ## ## ## ## ## ##
78
+ // ## ## ## ## ## ## ## ##
79
+ // ####### ## #### ######## #### ## ##
80
+
81
+ static int64_t power(int64_t base, int64_t exp)
82
+ {
83
+ int64_t result = 1;
84
+ while(exp)
85
+ {
86
+ result *= base; exp--;
87
+ }
88
+ return result;
89
+ }
90
+
91
+ #if defined(_MSC_VER)
92
+ #pragma intrinsic(_BitScanReverse64)
93
+ #endif
94
+
95
+ static int32_t get_bucket_index(const struct hdr_histogram* h, int64_t value)
96
+ {
97
+ #if defined(_MSC_VER)
98
+ uint32_t leading_zero = 0;
99
+ _BitScanReverse64(&leading_zero, value | h->sub_bucket_mask);
100
+ int32_t pow2ceiling = 64 - (63 - leading_zero); // smallest power of 2 containing value
101
+ #else
102
+ int32_t pow2ceiling = 64 - __builtin_clzll(value | h->sub_bucket_mask); // smallest power of 2 containing value
103
+ #endif
104
+ return pow2ceiling - h->unit_magnitude - (h->sub_bucket_half_count_magnitude + 1);
105
+ }
106
+
107
+ static int32_t get_sub_bucket_index(int64_t value, int32_t bucket_index, int32_t unit_magnitude)
108
+ {
109
+ return (int32_t)(value >> (bucket_index + unit_magnitude));
110
+ }
111
+
112
+ static int32_t counts_index(const struct hdr_histogram* h, int32_t bucket_index, int32_t sub_bucket_index)
113
+ {
114
+ // Calculate the index for the first entry in the bucket:
115
+ // (The following is the equivalent of ((bucket_index + 1) * subBucketHalfCount) ):
116
+ int32_t bucket_base_index = (bucket_index + 1) << h->sub_bucket_half_count_magnitude;
117
+ // Calculate the offset in the bucket:
118
+ int32_t offset_in_bucket = sub_bucket_index - h->sub_bucket_half_count;
119
+ // The following is the equivalent of ((sub_bucket_index - subBucketHalfCount) + bucketBaseIndex;
120
+ return bucket_base_index + offset_in_bucket;
121
+ }
122
+
123
+ static int64_t value_from_index(int32_t bucket_index, int32_t sub_bucket_index, int32_t unit_magnitude)
124
+ {
125
+ return ((int64_t) sub_bucket_index) << (bucket_index + unit_magnitude);
126
+ }
127
+
128
+ int32_t counts_index_for(const struct hdr_histogram* h, int64_t value)
129
+ {
130
+ int32_t bucket_index = get_bucket_index(h, value);
131
+ int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude);
132
+
133
+ return counts_index(h, bucket_index, sub_bucket_index);
134
+ }
135
+
136
+ int64_t hdr_value_at_index(const struct hdr_histogram *h, int32_t index)
137
+ {
138
+ int32_t bucket_index = (index >> h->sub_bucket_half_count_magnitude) - 1;
139
+ int32_t sub_bucket_index = (index & (h->sub_bucket_half_count - 1)) + h->sub_bucket_half_count;
140
+
141
+ if (bucket_index < 0)
142
+ {
143
+ sub_bucket_index -= h->sub_bucket_half_count;
144
+ bucket_index = 0;
145
+ }
146
+
147
+ return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude);
148
+ }
149
+
150
+ int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value)
151
+ {
152
+ int32_t bucket_index = get_bucket_index(h, value);
153
+ int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude);
154
+ int32_t adjusted_bucket = (sub_bucket_index >= h->sub_bucket_count) ? (bucket_index + 1) : bucket_index;
155
+ return INT64_C(1) << (h->unit_magnitude + adjusted_bucket);
156
+ }
157
+
158
+ static int64_t lowest_equivalent_value(const struct hdr_histogram* h, int64_t value)
159
+ {
160
+ int32_t bucket_index = get_bucket_index(h, value);
161
+ int32_t sub_bucket_index = get_sub_bucket_index(value, bucket_index, h->unit_magnitude);
162
+ return value_from_index(bucket_index, sub_bucket_index, h->unit_magnitude);
163
+ }
164
+
165
+ int64_t hdr_next_non_equivalent_value(const struct hdr_histogram *h, int64_t value)
166
+ {
167
+ return lowest_equivalent_value(h, value) + hdr_size_of_equivalent_value_range(h, value);
168
+ }
169
+
170
+ static int64_t highest_equivalent_value(const struct hdr_histogram* h, int64_t value)
171
+ {
172
+ return hdr_next_non_equivalent_value(h, value) - 1;
173
+ }
174
+
175
+ int64_t hdr_median_equivalent_value(const struct hdr_histogram *h, int64_t value)
176
+ {
177
+ return lowest_equivalent_value(h, value) + (hdr_size_of_equivalent_value_range(h, value) >> 1);
178
+ }
179
+
180
+ static int64_t non_zero_min(const struct hdr_histogram* h)
181
+ {
182
+ if (INT64_MAX == h->min_value)
183
+ {
184
+ return INT64_MAX;
185
+ }
186
+
187
+ return lowest_equivalent_value(h, h->min_value);
188
+ }
189
+
190
+ void hdr_reset_internal_counters(struct hdr_histogram* h)
191
+ {
192
+ int min_non_zero_index = -1;
193
+ int max_index = -1;
194
+ int64_t observed_total_count = 0;
195
+ int i;
196
+
197
+ for (i = 0; i < h->counts_len; i++)
198
+ {
199
+ int64_t count_at_index;
200
+
201
+ if ((count_at_index = counts_get_direct(h, i)) > 0)
202
+ {
203
+ observed_total_count += count_at_index;
204
+ max_index = i;
205
+ if (min_non_zero_index == -1 && i != 0)
206
+ {
207
+ min_non_zero_index = i;
208
+ }
209
+ }
210
+ }
211
+
212
+ if (max_index == -1)
213
+ {
214
+ h->max_value = 0;
215
+ }
216
+ else
217
+ {
218
+ int64_t max_value = hdr_value_at_index(h, max_index);
219
+ h->max_value = highest_equivalent_value(h, max_value);
220
+ }
221
+
222
+ if (min_non_zero_index == -1)
223
+ {
224
+ h->min_value = INT64_MAX;
225
+ }
226
+ else
227
+ {
228
+ h->min_value = hdr_value_at_index(h, min_non_zero_index);
229
+ }
230
+
231
+ h->total_count = observed_total_count;
232
+ }
233
+
234
+ static int32_t buckets_needed_to_cover_value(int64_t value, int32_t sub_bucket_count, int32_t unit_magnitude)
235
+ {
236
+ int64_t smallest_untrackable_value = ((int64_t) sub_bucket_count) << unit_magnitude;
237
+ int32_t buckets_needed = 1;
238
+ while (smallest_untrackable_value <= value)
239
+ {
240
+ if (smallest_untrackable_value > INT64_MAX / 2)
241
+ {
242
+ return buckets_needed + 1;
243
+ }
244
+ smallest_untrackable_value <<= 1;
245
+ buckets_needed++;
246
+ }
247
+
248
+ return buckets_needed;
249
+ }
250
+
251
+ // ## ## ######## ## ## ####### ######## ## ##
252
+ // ### ### ## ### ### ## ## ## ## ## ##
253
+ // #### #### ## #### #### ## ## ## ## ####
254
+ // ## ### ## ###### ## ### ## ## ## ######## ##
255
+ // ## ## ## ## ## ## ## ## ## ##
256
+ // ## ## ## ## ## ## ## ## ## ##
257
+ // ## ## ######## ## ## ####### ## ## ##
258
+
259
+ int hdr_calculate_bucket_config(
260
+ int64_t lowest_trackable_value,
261
+ int64_t highest_trackable_value,
262
+ int significant_figures,
263
+ struct hdr_histogram_bucket_config* cfg)
264
+ {
265
+ if (lowest_trackable_value < 1 ||
266
+ significant_figures < 1 || 5 < significant_figures)
267
+ {
268
+ return EINVAL;
269
+ }
270
+ else if (lowest_trackable_value * 2 > highest_trackable_value)
271
+ {
272
+ return EINVAL;
273
+ }
274
+
275
+ cfg->lowest_trackable_value = lowest_trackable_value;
276
+ cfg->significant_figures = significant_figures;
277
+ cfg->highest_trackable_value = highest_trackable_value;
278
+
279
+ int64_t largest_value_with_single_unit_resolution = 2 * power(10, significant_figures);
280
+ int32_t sub_bucket_count_magnitude = (int32_t) ceil(log((double)largest_value_with_single_unit_resolution) / log(2));
281
+ cfg->sub_bucket_half_count_magnitude = ((sub_bucket_count_magnitude > 1) ? sub_bucket_count_magnitude : 1) - 1;
282
+
283
+ cfg->unit_magnitude = (int32_t) floor(log((double)lowest_trackable_value) / log(2));
284
+
285
+ cfg->sub_bucket_count = (int32_t) pow(2, (cfg->sub_bucket_half_count_magnitude + 1));
286
+ cfg->sub_bucket_half_count = cfg->sub_bucket_count / 2;
287
+ cfg->sub_bucket_mask = ((int64_t) cfg->sub_bucket_count - 1) << cfg->unit_magnitude;
288
+
289
+ // determine exponent range needed to support the trackable value with no overflow:
290
+ cfg->bucket_count = buckets_needed_to_cover_value(highest_trackable_value, cfg->sub_bucket_count, (int32_t)cfg->unit_magnitude);
291
+ cfg->counts_len = (cfg->bucket_count + 1) * (cfg->sub_bucket_count / 2);
292
+
293
+ return 0;
294
+ }
295
+
296
+ void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg)
297
+ {
298
+ h->lowest_trackable_value = cfg->lowest_trackable_value;
299
+ h->highest_trackable_value = cfg->highest_trackable_value;
300
+ h->unit_magnitude = (int32_t)cfg->unit_magnitude;
301
+ h->significant_figures = (int32_t)cfg->significant_figures;
302
+ h->sub_bucket_half_count_magnitude = cfg->sub_bucket_half_count_magnitude;
303
+ h->sub_bucket_half_count = cfg->sub_bucket_half_count;
304
+ h->sub_bucket_mask = cfg->sub_bucket_mask;
305
+ h->sub_bucket_count = cfg->sub_bucket_count;
306
+ h->min_value = INT64_MAX;
307
+ h->max_value = 0;
308
+ h->normalizing_index_offset = 0;
309
+ h->conversion_ratio = 1.0;
310
+ h->bucket_count = cfg->bucket_count;
311
+ h->counts_len = cfg->counts_len;
312
+ h->total_count = 0;
313
+ }
314
+
315
+ int hdr_init(
316
+ int64_t lowest_trackable_value,
317
+ int64_t highest_trackable_value,
318
+ int significant_figures,
319
+ struct hdr_histogram** result)
320
+ {
321
+ struct hdr_histogram_bucket_config cfg;
322
+
323
+ int r = hdr_calculate_bucket_config(lowest_trackable_value, highest_trackable_value, significant_figures, &cfg);
324
+ if (r)
325
+ {
326
+ return r;
327
+ }
328
+
329
+ size_t histogram_size = sizeof(struct hdr_histogram) + cfg.counts_len * sizeof(int64_t);
330
+ struct hdr_histogram* histogram = malloc(histogram_size);
331
+
332
+ if (!histogram)
333
+ {
334
+ return ENOMEM;
335
+ }
336
+
337
+ // memset will ensure that all of the function pointers are null.
338
+ memset((void*) histogram, 0, histogram_size);
339
+
340
+ hdr_init_preallocated(histogram, &cfg);
341
+ *result = histogram;
342
+
343
+ return 0;
344
+ }
345
+
346
+
347
+ int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result)
348
+ {
349
+ return hdr_init(1, highest_trackable_value, significant_figures, result);
350
+ }
351
+
352
+ // reset a histogram to zero.
353
+ void hdr_reset(struct hdr_histogram *h)
354
+ {
355
+ h->total_count=0;
356
+ h->min_value = INT64_MAX;
357
+ h->max_value = 0;
358
+ memset((void *) &h->counts, 0, (sizeof(int64_t) * h->counts_len));
359
+ return;
360
+ }
361
+
362
+ size_t hdr_get_memory_size(struct hdr_histogram *h)
363
+ {
364
+ return sizeof(struct hdr_histogram) + h->counts_len * sizeof(int64_t);
365
+ }
366
+
367
+ // ## ## ######## ######## ### ######## ######## ######
368
+ // ## ## ## ## ## ## ## ## ## ## ## ##
369
+ // ## ## ## ## ## ## ## ## ## ## ##
370
+ // ## ## ######## ## ## ## ## ## ###### ######
371
+ // ## ## ## ## ## ######### ## ## ##
372
+ // ## ## ## ## ## ## ## ## ## ## ##
373
+ // ####### ## ######## ## ## ## ######## ######
374
+
375
+
376
+ bool hdr_record_value(struct hdr_histogram* h, int64_t value)
377
+ {
378
+ return hdr_record_values(h, value, 1);
379
+ }
380
+
381
+ bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count)
382
+ {
383
+ if (value < 0)
384
+ {
385
+ return false;
386
+ }
387
+
388
+ int32_t counts_index = counts_index_for(h, value);
389
+
390
+ if (counts_index < 0 || h->counts_len <= counts_index)
391
+ {
392
+ return false;
393
+ }
394
+
395
+ counts_inc_normalised(h, counts_index, count);
396
+ update_min_max(h, value);
397
+
398
+ return true;
399
+ }
400
+
401
+ bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expected_interval)
402
+ {
403
+ return hdr_record_corrected_values(h, value, 1, expected_interval);
404
+ }
405
+
406
+
407
+ bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval)
408
+ {
409
+ if (!hdr_record_values(h, value, count))
410
+ {
411
+ return false;
412
+ }
413
+
414
+ if (expected_interval <= 0 || value <= expected_interval)
415
+ {
416
+ return true;
417
+ }
418
+
419
+ int64_t missing_value = value - expected_interval;
420
+ for (; missing_value >= expected_interval; missing_value -= expected_interval)
421
+ {
422
+ if (!hdr_record_values(h, missing_value, count))
423
+ {
424
+ return false;
425
+ }
426
+ }
427
+
428
+ return true;
429
+ }
430
+
431
+ int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from)
432
+ {
433
+ struct hdr_iter iter;
434
+ hdr_iter_recorded_init(&iter, from);
435
+ int64_t dropped = 0;
436
+
437
+ while (hdr_iter_next(&iter))
438
+ {
439
+ int64_t value = iter.value;
440
+ int64_t count = iter.count;
441
+
442
+ if (!hdr_record_values(h, value, count))
443
+ {
444
+ dropped += count;
445
+ }
446
+ }
447
+
448
+ return dropped;
449
+ }
450
+
451
+ int64_t hdr_add_while_correcting_for_coordinated_omission(
452
+ struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval)
453
+ {
454
+ struct hdr_iter iter;
455
+ hdr_iter_recorded_init(&iter, from);
456
+ int64_t dropped = 0;
457
+
458
+ while (hdr_iter_next(&iter))
459
+ {
460
+ int64_t value = iter.value;
461
+ int64_t count = iter.count;
462
+
463
+ if (!hdr_record_corrected_values(h, value, count, expected_interval))
464
+ {
465
+ dropped += count;
466
+ }
467
+ }
468
+
469
+ return dropped;
470
+ }
471
+
472
+
473
+
474
+ // ## ## ### ## ## ## ######## ######
475
+ // ## ## ## ## ## ## ## ## ## ##
476
+ // ## ## ## ## ## ## ## ## ##
477
+ // ## ## ## ## ## ## ## ###### ######
478
+ // ## ## ######### ## ## ## ## ##
479
+ // ## ## ## ## ## ## ## ## ## ##
480
+ // ### ## ## ######## ####### ######## ######
481
+
482
+
483
+ int64_t hdr_max(const struct hdr_histogram* h)
484
+ {
485
+ if (0 == h->max_value)
486
+ {
487
+ return 0;
488
+ }
489
+
490
+ return highest_equivalent_value(h, h->max_value);
491
+ }
492
+
493
+ int64_t hdr_min(const struct hdr_histogram* h)
494
+ {
495
+ if (0 < hdr_count_at_index(h, 0))
496
+ {
497
+ return 0;
498
+ }
499
+
500
+ return non_zero_min(h);
501
+ }
502
+
503
+ int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile)
504
+ {
505
+ struct hdr_iter iter;
506
+ hdr_iter_init(&iter, h);
507
+
508
+ double requested_percentile = percentile < 100.0 ? percentile : 100.0;
509
+ int64_t count_at_percentile =
510
+ (int64_t) (((requested_percentile / 100) * h->total_count) + 0.5);
511
+ count_at_percentile = count_at_percentile > 1 ? count_at_percentile : 1;
512
+ int64_t total = 0;
513
+
514
+ while (hdr_iter_next(&iter))
515
+ {
516
+ total += iter.count;
517
+
518
+ if (total >= count_at_percentile)
519
+ {
520
+ int64_t value_from_index = iter.value;
521
+ return highest_equivalent_value(h, value_from_index);
522
+ }
523
+ }
524
+
525
+ return 0;
526
+ }
527
+
528
+ double hdr_mean(const struct hdr_histogram* h)
529
+ {
530
+ struct hdr_iter iter;
531
+ int64_t total = 0;
532
+
533
+ hdr_iter_init(&iter, h);
534
+
535
+ while (hdr_iter_next(&iter))
536
+ {
537
+ if (0 != iter.count)
538
+ {
539
+ total += iter.count * hdr_median_equivalent_value(h, iter.value);
540
+ }
541
+ }
542
+
543
+ return (total * 1.0) / h->total_count;
544
+ }
545
+
546
+ double hdr_stddev(const struct hdr_histogram* h)
547
+ {
548
+ double mean = hdr_mean(h);
549
+ double geometric_dev_total = 0.0;
550
+
551
+ struct hdr_iter iter;
552
+ hdr_iter_init(&iter, h);
553
+
554
+ while (hdr_iter_next(&iter))
555
+ {
556
+ if (0 != iter.count)
557
+ {
558
+ double dev = (hdr_median_equivalent_value(h, iter.value) * 1.0) - mean;
559
+ geometric_dev_total += (dev * dev) * iter.count;
560
+ }
561
+ }
562
+
563
+ return sqrt(geometric_dev_total / h->total_count);
564
+ }
565
+
566
+ bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b)
567
+ {
568
+ return lowest_equivalent_value(h, a) == lowest_equivalent_value(h, b);
569
+ }
570
+
571
+ int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value)
572
+ {
573
+ return lowest_equivalent_value(h, value);
574
+ }
575
+
576
+ int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value)
577
+ {
578
+ return counts_get_normalised(h, counts_index_for(h, value));
579
+ }
580
+
581
+ int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index)
582
+ {
583
+ return counts_get_normalised(h, index);
584
+ }
585
+
586
+
587
+ // #### ######## ######## ######## ### ######## ####### ######## ######
588
+ // ## ## ## ## ## ## ## ## ## ## ## ## ## ##
589
+ // ## ## ## ## ## ## ## ## ## ## ## ## ##
590
+ // ## ## ###### ######## ## ## ## ## ## ######## ######
591
+ // ## ## ## ## ## ######### ## ## ## ## ## ##
592
+ // ## ## ## ## ## ## ## ## ## ## ## ## ## ##
593
+ // #### ## ######## ## ## ## ## ## ####### ## ## ######
594
+
595
+
596
+ static bool has_buckets(struct hdr_iter* iter)
597
+ {
598
+ return iter->counts_index < iter->h->counts_len;
599
+ }
600
+
601
+ static bool has_next(struct hdr_iter* iter)
602
+ {
603
+ return iter->cumulative_count < iter->h->total_count;
604
+ }
605
+
606
+ static bool move_next(struct hdr_iter* iter)
607
+ {
608
+ iter->counts_index++;
609
+
610
+ if (!has_buckets(iter))
611
+ {
612
+ return false;
613
+ }
614
+
615
+ iter->count = counts_get_normalised(iter->h, iter->counts_index);
616
+ iter->cumulative_count += iter->count;
617
+
618
+ iter->value = hdr_value_at_index(iter->h, iter->counts_index);
619
+ iter->highest_equivalent_value = highest_equivalent_value(iter->h, iter->value);
620
+ iter->lowest_equivalent_value = lowest_equivalent_value(iter->h, iter->value);
621
+ iter->median_equivalent_value = hdr_median_equivalent_value(iter->h, iter->value);
622
+
623
+ return true;
624
+ }
625
+
626
+ static int64_t peek_next_value_from_index(struct hdr_iter* iter)
627
+ {
628
+ return hdr_value_at_index(iter->h, iter->counts_index + 1);
629
+ }
630
+
631
+ static bool next_value_greater_than_reporting_level_upper_bound(
632
+ struct hdr_iter *iter, int64_t reporting_level_upper_bound)
633
+ {
634
+ if (iter->counts_index >= iter->h->counts_len)
635
+ {
636
+ return false;
637
+ }
638
+
639
+ return peek_next_value_from_index(iter) > reporting_level_upper_bound;
640
+ }
641
+
642
+ static bool _basic_iter_next(struct hdr_iter *iter)
643
+ {
644
+ if (!has_next(iter))
645
+ {
646
+ return false;
647
+ }
648
+
649
+ move_next(iter);
650
+
651
+ return true;
652
+ }
653
+
654
+ static void _update_iterated_values(struct hdr_iter* iter, int64_t new_value_iterated_to)
655
+ {
656
+ iter->value_iterated_from = iter->value_iterated_to;
657
+ iter->value_iterated_to = new_value_iterated_to;
658
+ }
659
+
660
+ static bool _all_values_iter_next(struct hdr_iter* iter)
661
+ {
662
+ bool result = move_next(iter);
663
+
664
+ if (result)
665
+ {
666
+ _update_iterated_values(iter, iter->value);
667
+ }
668
+
669
+ return result;
670
+ }
671
+
672
+ void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h)
673
+ {
674
+ iter->h = h;
675
+
676
+ iter->counts_index = -1;
677
+ iter->count = 0;
678
+ iter->cumulative_count = 0;
679
+ iter->value = 0;
680
+ iter->highest_equivalent_value = 0;
681
+ iter->value_iterated_from = 0;
682
+ iter->value_iterated_to = 0;
683
+
684
+ iter->_next_fp = _all_values_iter_next;
685
+ }
686
+
687
+ bool hdr_iter_next(struct hdr_iter* iter)
688
+ {
689
+ return iter->_next_fp(iter);
690
+ }
691
+
692
+ // ######## ######## ######## ###### ######## ## ## ######## #### ## ######## ######
693
+ // ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## ##
694
+ // ## ## ## ## ## ## ## #### ## ## ## ## ## ##
695
+ // ######## ###### ######## ## ###### ## ## ## ## ## ## ###### ######
696
+ // ## ## ## ## ## ## ## #### ## ## ## ## ##
697
+ // ## ## ## ## ## ## ## ## ### ## ## ## ## ## ##
698
+ // ## ######## ## ## ###### ######## ## ## ## #### ######## ######## ######
699
+
700
+ static bool _percentile_iter_next(struct hdr_iter* iter)
701
+ {
702
+ struct hdr_iter_percentiles* percentiles = &iter->specifics.percentiles;
703
+
704
+ if (!has_next(iter))
705
+ {
706
+ if (percentiles->seen_last_value)
707
+ {
708
+ return false;
709
+ }
710
+
711
+ percentiles->seen_last_value = true;
712
+ percentiles->percentile = 100.0;
713
+
714
+ return true;
715
+ }
716
+
717
+ if (iter->counts_index == -1 && !_basic_iter_next(iter))
718
+ {
719
+ return false;
720
+ }
721
+
722
+ do
723
+ {
724
+ double current_percentile = (100.0 * (double) iter->cumulative_count) / iter->h->total_count;
725
+ if (iter->count != 0 &&
726
+ percentiles->percentile_to_iterate_to <= current_percentile)
727
+ {
728
+ _update_iterated_values(iter, highest_equivalent_value(iter->h, iter->value));
729
+
730
+ percentiles->percentile = percentiles->percentile_to_iterate_to;
731
+ int64_t temp = (int64_t)(log(100 / (100.0 - (percentiles->percentile_to_iterate_to))) / log(2)) + 1;
732
+ int64_t half_distance = (int64_t) pow(2, (double) temp);
733
+ int64_t percentile_reporting_ticks = percentiles->ticks_per_half_distance * half_distance;
734
+ percentiles->percentile_to_iterate_to += 100.0 / percentile_reporting_ticks;
735
+
736
+ return true;
737
+ }
738
+ }
739
+ while (_basic_iter_next(iter));
740
+
741
+ return true;
742
+ }
743
+
744
+ void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance)
745
+ {
746
+ iter->h = h;
747
+
748
+ hdr_iter_init(iter, h);
749
+
750
+ iter->specifics.percentiles.seen_last_value = false;
751
+ iter->specifics.percentiles.ticks_per_half_distance = ticks_per_half_distance;
752
+ iter->specifics.percentiles.percentile_to_iterate_to = 0.0;
753
+ iter->specifics.percentiles.percentile = 0.0;
754
+
755
+ iter->_next_fp = _percentile_iter_next;
756
+ }
757
+
758
+ static void format_line_string(char* str, size_t len, int significant_figures, format_type format)
759
+ {
760
+ #if defined(_MSC_VER)
761
+ #define snprintf _snprintf
762
+ #pragma warning(push)
763
+ #pragma warning(disable: 4996)
764
+ #endif
765
+ const char* format_str = "%s%d%s";
766
+
767
+ switch (format)
768
+ {
769
+ case CSV:
770
+ snprintf(str, len, format_str, "%.", significant_figures, "f,%f,%d,%.2f\n");
771
+ break;
772
+ case CLASSIC:
773
+ snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n");
774
+ break;
775
+ default:
776
+ snprintf(str, len, format_str, "%12.", significant_figures, "f %12f %12d %12.2f\n");
777
+ }
778
+ #if defined(_MSC_VER)
779
+ #undef snprintf
780
+ #pragma warning(pop)
781
+ #endif
782
+ }
783
+
784
+
785
+ // ######## ######## ###### ####### ######## ######## ######## ########
786
+ // ## ## ## ## ## ## ## ## ## ## ## ## ## ##
787
+ // ## ## ## ## ## ## ## ## ## ## ## ## ##
788
+ // ######## ###### ## ## ## ######## ## ## ###### ## ##
789
+ // ## ## ## ## ## ## ## ## ## ## ## ## ##
790
+ // ## ## ## ## ## ## ## ## ## ## ## ## ## ##
791
+ // ## ## ######## ###### ####### ## ## ######## ######## ########
792
+
793
+
794
+ static bool _recorded_iter_next(struct hdr_iter* iter)
795
+ {
796
+ while (_basic_iter_next(iter))
797
+ {
798
+ if (iter->count != 0)
799
+ {
800
+ _update_iterated_values(iter, iter->value);
801
+
802
+ iter->specifics.recorded.count_added_in_this_iteration_step = iter->count;
803
+ return true;
804
+ }
805
+ }
806
+
807
+ return false;
808
+ }
809
+
810
+ void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h)
811
+ {
812
+ hdr_iter_init(iter, h);
813
+
814
+ iter->specifics.recorded.count_added_in_this_iteration_step = 0;
815
+
816
+ iter->_next_fp = _recorded_iter_next;
817
+ }
818
+
819
+ // ## #### ## ## ######## ### ########
820
+ // ## ## ### ## ## ## ## ## ##
821
+ // ## ## #### ## ## ## ## ## ##
822
+ // ## ## ## ## ## ###### ## ## ########
823
+ // ## ## ## #### ## ######### ## ##
824
+ // ## ## ## ### ## ## ## ## ##
825
+ // ######## #### ## ## ######## ## ## ## ##
826
+
827
+
828
+ static bool _iter_linear_next(struct hdr_iter* iter)
829
+ {
830
+ struct hdr_iter_linear* linear = &iter->specifics.linear;
831
+
832
+ linear->count_added_in_this_iteration_step = 0;
833
+
834
+ if (has_next(iter) ||
835
+ next_value_greater_than_reporting_level_upper_bound(
836
+ iter, linear->next_value_reporting_level_lowest_equivalent))
837
+ {
838
+ do
839
+ {
840
+ if (iter->value >= linear->next_value_reporting_level_lowest_equivalent)
841
+ {
842
+ _update_iterated_values(iter, linear->next_value_reporting_level);
843
+
844
+ linear->next_value_reporting_level += linear->value_units_per_bucket;
845
+ linear->next_value_reporting_level_lowest_equivalent =
846
+ lowest_equivalent_value(iter->h, linear->next_value_reporting_level);
847
+
848
+ return true;
849
+ }
850
+
851
+ if (!move_next(iter))
852
+ {
853
+ return true;
854
+ }
855
+
856
+ linear->count_added_in_this_iteration_step += iter->count;
857
+ }
858
+ while (true);
859
+ }
860
+
861
+ return false;
862
+ }
863
+
864
+
865
+ void hdr_iter_linear_init(struct hdr_iter* iter, const struct hdr_histogram* h, int64_t value_units_per_bucket)
866
+ {
867
+ hdr_iter_init(iter, h);
868
+
869
+ iter->specifics.linear.count_added_in_this_iteration_step = 0;
870
+ iter->specifics.linear.value_units_per_bucket = value_units_per_bucket;
871
+ iter->specifics.linear.next_value_reporting_level = value_units_per_bucket;
872
+ iter->specifics.linear.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_per_bucket);
873
+
874
+ iter->_next_fp = _iter_linear_next;
875
+ }
876
+
877
+ // ## ####### ###### ### ######## #### ######## ## ## ## ## #### ######
878
+ // ## ## ## ## ## ## ## ## ## ## ## ## ## ### ### ## ## ##
879
+ // ## ## ## ## ## ## ## ## ## ## ## ## #### #### ## ##
880
+ // ## ## ## ## #### ## ## ######## ## ## ######### ## ### ## ## ##
881
+ // ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ##
882
+ // ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
883
+ // ######## ####### ###### ## ## ## ## #### ## ## ## ## ## #### ######
884
+
885
+ static bool _log_iter_next(struct hdr_iter *iter)
886
+ {
887
+ struct hdr_iter_log* logarithmic = &iter->specifics.log;
888
+
889
+ logarithmic->count_added_in_this_iteration_step = 0;
890
+
891
+ if (has_next(iter) ||
892
+ next_value_greater_than_reporting_level_upper_bound(
893
+ iter, logarithmic->next_value_reporting_level_lowest_equivalent))
894
+ {
895
+ do
896
+ {
897
+ if (iter->value >= logarithmic->next_value_reporting_level_lowest_equivalent)
898
+ {
899
+ _update_iterated_values(iter, logarithmic->next_value_reporting_level);
900
+
901
+ logarithmic->next_value_reporting_level *= (int64_t)logarithmic->log_base;
902
+ logarithmic->next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(iter->h, logarithmic->next_value_reporting_level);
903
+
904
+ return true;
905
+ }
906
+
907
+ if (!move_next(iter))
908
+ {
909
+ return true;
910
+ }
911
+
912
+ logarithmic->count_added_in_this_iteration_step += iter->count;
913
+ }
914
+ while (true);
915
+ }
916
+
917
+ return false;
918
+ }
919
+
920
+ void hdr_iter_log_init(
921
+ struct hdr_iter* iter,
922
+ const struct hdr_histogram* h,
923
+ int64_t value_units_first_bucket,
924
+ double log_base)
925
+ {
926
+ hdr_iter_init(iter, h);
927
+ iter->specifics.log.count_added_in_this_iteration_step = 0;
928
+ iter->specifics.log.log_base = log_base;
929
+ iter->specifics.log.next_value_reporting_level = value_units_first_bucket;
930
+ iter->specifics.log.next_value_reporting_level_lowest_equivalent = lowest_equivalent_value(h, value_units_first_bucket);
931
+
932
+ iter->_next_fp = _log_iter_next;
933
+ }
934
+
935
+ // Printing.
936
+
937
+ static const char* format_head_string(format_type format)
938
+ {
939
+ switch (format)
940
+ {
941
+ case CSV:
942
+ return "%s,%s,%s,%s\n";
943
+ case CLASSIC:
944
+ return "%12s %12s %12s %12s\n\n";
945
+ default:
946
+ return "%12s %12s %12s %12s\n\n";
947
+ }
948
+ }
949
+
950
+ static const char CLASSIC_FOOTER[] =
951
+ "#[Mean = %12.3f, StdDeviation = %12.3f]\n"
952
+ "#[Max = %12.3f, Total count = %12" PRIu64 "]\n"
953
+ "#[Buckets = %12d, SubBuckets = %12d]\n";
954
+
955
+ int hdr_percentiles_print(
956
+ struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance,
957
+ double value_scale, format_type format)
958
+ {
959
+ char line_format[25];
960
+ format_line_string(line_format, 25, h->significant_figures, format);
961
+ const char* head_format = format_head_string(format);
962
+ int rc = 0;
963
+
964
+ struct hdr_iter iter;
965
+ hdr_iter_percentile_init(&iter, h, ticks_per_half_distance);
966
+
967
+ if (fprintf(
968
+ stream, head_format,
969
+ "Value", "Percentile", "TotalCount", "1/(1-Percentile)") < 0)
970
+ {
971
+ rc = EIO;
972
+ goto cleanup;
973
+ }
974
+
975
+ struct hdr_iter_percentiles * percentiles = &iter.specifics.percentiles;
976
+ while (hdr_iter_next(&iter))
977
+ {
978
+ double value = iter.highest_equivalent_value / value_scale;
979
+ double percentile = percentiles->percentile / 100.0;
980
+ int64_t total_count = iter.cumulative_count;
981
+ double inverted_percentile = (1.0 / (1.0 - percentile));
982
+
983
+ if (fprintf(
984
+ stream, line_format, value, percentile, total_count, inverted_percentile) < 0)
985
+ {
986
+ rc = EIO;
987
+ goto cleanup;
988
+ }
989
+ }
990
+
991
+ if (CLASSIC == format)
992
+ {
993
+ double mean = hdr_mean(h) / value_scale;
994
+ double stddev = hdr_stddev(h) / value_scale;
995
+ double max = hdr_max(h) / value_scale;
996
+
997
+ if (fprintf(
998
+ stream, CLASSIC_FOOTER, mean, stddev, max,
999
+ h->total_count, h->bucket_count, h->sub_bucket_count) < 0)
1000
+ {
1001
+ rc = EIO;
1002
+ goto cleanup;
1003
+ }
1004
+ }
1005
+
1006
+ cleanup:
1007
+ return rc;
1008
+ }