HDRHistogram 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +7 -0
- data/Gemfile +4 -0
- data/HDRHistogram.gemspec +37 -0
- data/LICENSE.txt +21 -0
- data/README.md +144 -0
- data/Rakefile +5 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/ruby_hdr_histogram/extconf.rb +6 -0
- data/ext/ruby_hdr_histogram/hdr_histogram.c +1008 -0
- data/ext/ruby_hdr_histogram/hdr_histogram.h +415 -0
- data/ext/ruby_hdr_histogram/ruby_hdr_histogram.c +152 -0
- data/lib/HDRHistogram.rb +65 -0
- data/lib/HDRHistogram/version.rb +3 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -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,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
|
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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`
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -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
|
data/bin/setup
ADDED
@@ -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
|
+
}
|