Skip to content

Commit 355fced

Browse files
committed
Improve Summary and Histogram API. Add constructors from rvalue references, observe from templated input it
1 parent 722fbcf commit 355fced

File tree

5 files changed

+86
-27
lines changed

5 files changed

+86
-27
lines changed

core/include/prometheus/histogram.h

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ class PROMETHEUS_CPP_CORE_EXPORT Histogram {
4444
/// exponential etc..
4545
///
4646
/// The bucket boundaries cannot be changed once the histogram is created.
47-
Histogram(const BucketBoundaries& buckets);
47+
explicit Histogram(const BucketBoundaries& buckets);
48+
49+
explicit Histogram(BucketBoundaries&& buckets);
4850

4951
/// \brief Observe the given amount.
5052
///
@@ -60,15 +62,36 @@ class PROMETHEUS_CPP_CORE_EXPORT Histogram {
6062
/// this function must have already sorted the values into buckets).
6163
/// Also increments the total sum of all observations by the given value.
6264
void ObserveMultiple(const std::vector<double>& bucket_increments,
63-
const double sum_of_values);
65+
double sum_of_values);
66+
67+
/// Same as above with custom iterator type.
68+
template <class InputIt>
69+
void ObserveMultiple(InputIt from, InputIt end, double sum_of_values) {
70+
std::lock_guard<std::mutex> lock(mutex_);
71+
sum_.Increment(sum_of_values);
72+
73+
for (std::size_t i{0}; i < bucket_counts_.size(); ++i, ++from) {
74+
if (from == end) {
75+
throw std::length_error(
76+
"The size of bucket_increments should be equal to "
77+
"the number of buckets in the histogram.");
78+
}
79+
bucket_counts_[i].Increment(*from);
80+
}
81+
if (from != end) {
82+
throw std::length_error(
83+
"The size of bucket_increments should be equal to "
84+
"the number of buckets in the histogram.");
85+
}
86+
}
6487

6588
/// \brief Get the current value of the counter.
6689
///
6790
/// Collect is called by the Registry when collecting metrics.
6891
ClientMetric Collect() const;
6992

7093
private:
71-
const BucketBoundaries bucket_boundaries_;
94+
BucketBoundaries bucket_boundaries_;
7295
mutable std::mutex mutex_;
7396
std::vector<Counter> bucket_counts_;
7497
Gauge sum_;

core/include/prometheus/summary.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,13 @@ class PROMETHEUS_CPP_CORE_EXPORT Summary {
7171
/// and how smooth the time window is moved. With only one age bucket it
7272
/// effectively results in a complete reset of the summary each time max_age
7373
/// has passed. The default value is 5.
74-
Summary(const Quantiles& quantiles,
75-
std::chrono::milliseconds max_age = std::chrono::seconds{60},
76-
int age_buckets = 5);
74+
explicit Summary(const Quantiles& quantiles,
75+
std::chrono::milliseconds max_age = std::chrono::seconds{60},
76+
int age_buckets = 5);
77+
78+
explicit Summary(Quantiles&& quantiles,
79+
std::chrono::milliseconds max_age = std::chrono::seconds{60},
80+
int age_buckets = 5);
7781

7882
/// \brief Observe the given amount.
7983
void Observe(double value);
@@ -84,10 +88,10 @@ class PROMETHEUS_CPP_CORE_EXPORT Summary {
8488
ClientMetric Collect() const;
8589

8690
private:
87-
const Quantiles quantiles_;
91+
Quantiles quantiles_;
8892
mutable std::mutex mutex_;
89-
std::uint64_t count_;
90-
double sum_;
93+
std::uint64_t count_ = 0;
94+
double sum_ = 0;
9195
detail::TimeWindowQuantiles quantile_values_;
9296
};
9397

core/src/histogram.cc

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,30 @@
1212
namespace prometheus {
1313

1414
Histogram::Histogram(const BucketBoundaries& buckets)
15-
: bucket_boundaries_{buckets}, bucket_counts_{buckets.size() + 1}, sum_{} {
15+
: bucket_boundaries_{buckets}, bucket_counts_{buckets.size() + 1} {
1616
assert(std::is_sorted(std::begin(bucket_boundaries_),
1717
std::end(bucket_boundaries_)));
1818
}
1919

20-
void Histogram::Observe(const double value) {
21-
// TODO: determine bucket list size at which binary search would be faster
22-
const auto bucket_index = static_cast<std::size_t>(std::distance(
23-
bucket_boundaries_.begin(),
24-
std::find_if(
25-
std::begin(bucket_boundaries_), std::end(bucket_boundaries_),
26-
[value](const double boundary) { return boundary >= value; })));
20+
Histogram::Histogram(BucketBoundaries&& buckets)
21+
: bucket_boundaries_(std::move(buckets)),
22+
bucket_counts_{bucket_boundaries_.size() + 1} {
23+
assert(std::is_sorted(bucket_boundaries_.begin(), bucket_boundaries_.end()));
24+
}
25+
26+
void Histogram::Observe(double value) {
27+
const auto bucket_index = static_cast<std::size_t>(
28+
std::distance(bucket_boundaries_.begin(),
29+
std::lower_bound(bucket_boundaries_.begin(),
30+
bucket_boundaries_.end(), value)));
2731

2832
std::lock_guard<std::mutex> lock(mutex_);
2933
sum_.Increment(value);
3034
bucket_counts_[bucket_index].Increment();
3135
}
3236

3337
void Histogram::ObserveMultiple(const std::vector<double>& bucket_increments,
34-
const double sum_of_values) {
38+
double sum_of_values) {
3539
if (bucket_increments.size() != bucket_counts_.size()) {
3640
throw std::length_error(
3741
"The size of bucket_increments was not equal to"
@@ -49,13 +53,13 @@ void Histogram::ObserveMultiple(const std::vector<double>& bucket_increments,
4953
ClientMetric Histogram::Collect() const {
5054
std::lock_guard<std::mutex> lock(mutex_);
5155

52-
auto metric = ClientMetric{};
56+
ClientMetric metric{};
5357

5458
auto cumulative_count = 0ULL;
5559
metric.histogram.bucket.reserve(bucket_counts_.size());
5660
for (std::size_t i{0}; i < bucket_counts_.size(); ++i) {
5761
cumulative_count += bucket_counts_[i].Value();
58-
auto bucket = ClientMetric::Bucket{};
62+
ClientMetric::Bucket bucket{};
5963
bucket.cumulative_count = cumulative_count;
6064
bucket.upper_bound = (i == bucket_boundaries_.size()
6165
? std::numeric_limits<double>::infinity()

core/src/summary.cc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ namespace prometheus {
77
Summary::Summary(const Quantiles& quantiles,
88
const std::chrono::milliseconds max_age, const int age_buckets)
99
: quantiles_{quantiles},
10-
count_{0},
11-
sum_{0},
1210
quantile_values_{quantiles_, max_age, age_buckets} {}
1311

14-
void Summary::Observe(const double value) {
12+
Summary::Summary(Quantiles&& quantiles, const std::chrono::milliseconds max_age,
13+
const int age_buckets)
14+
: quantiles_(std::move(quantiles)),
15+
quantile_values_{quantiles_, max_age, age_buckets} {}
16+
17+
void Summary::Observe(double value) {
1518
std::lock_guard<std::mutex> lock(mutex_);
1619

1720
count_ += 1;
@@ -20,13 +23,13 @@ void Summary::Observe(const double value) {
2023
}
2124

2225
ClientMetric Summary::Collect() const {
23-
auto metric = ClientMetric{};
26+
ClientMetric metric{};
2427

2528
std::lock_guard<std::mutex> lock(mutex_);
2629

2730
metric.summary.quantile.reserve(quantiles_.size());
2831
for (const auto& quantile : quantiles_) {
29-
auto metricQuantile = ClientMetric::Quantile{};
32+
ClientMetric::Quantile metricQuantile{};
3033
metricQuantile.quantile = quantile.quantile;
3134
metricQuantile.value = quantile_values_.get(quantile.quantile);
3235
metric.summary.quantile.push_back(std::move(metricQuantile));

core/tests/histogram_test.cc

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <gtest/gtest.h>
44

5+
#include <forward_list>
56
#include <limits>
67
#include <memory>
78
#include <stdexcept>
@@ -81,7 +82,7 @@ TEST(HistogramTest, cumulative_bucket_count) {
8182
EXPECT_EQ(h.bucket.at(2).cumulative_count, 7U);
8283
}
8384

84-
TEST(HistogramTest, observe_multiple_test_bucket_counts) {
85+
TEST(HistogramTest, observe_multiple_test_bucket_counts_1) {
8586
Histogram histogram{{1, 2}};
8687
histogram.ObserveMultiple({5, 9, 3}, 20);
8788
histogram.ObserveMultiple({0, 20, 6}, 34);
@@ -93,6 +94,20 @@ TEST(HistogramTest, observe_multiple_test_bucket_counts) {
9394
EXPECT_EQ(h.bucket.at(2).cumulative_count, 43U);
9495
}
9596

97+
TEST(HistogramTest, observe_multiple_test_bucket_counts_2) {
98+
Histogram histogram{{1, 2}};
99+
const std::forward_list<double> values1 = {5, 9, 3};
100+
const std::forward_list<double> values2 = {0, 20, 6};
101+
histogram.ObserveMultiple(values1.begin(), values1.end(), 20);
102+
histogram.ObserveMultiple(values2.begin(), values2.end(), 34);
103+
auto metric = histogram.Collect();
104+
auto h = metric.histogram;
105+
ASSERT_EQ(h.bucket.size(), 3U);
106+
EXPECT_EQ(h.bucket.at(0).cumulative_count, 5U);
107+
EXPECT_EQ(h.bucket.at(1).cumulative_count, 34U);
108+
EXPECT_EQ(h.bucket.at(2).cumulative_count, 43U);
109+
}
110+
96111
TEST(HistogramTest, observe_multiple_test_total_sum) {
97112
Histogram histogram{{1, 2}};
98113
histogram.ObserveMultiple({5, 9, 3}, 20);
@@ -103,13 +118,23 @@ TEST(HistogramTest, observe_multiple_test_total_sum) {
103118
EXPECT_EQ(h.sample_sum, 54);
104119
}
105120

106-
TEST(HistogramTest, observe_multiple_test_length_error) {
121+
TEST(HistogramTest, observe_multiple_test_length_error1) {
107122
Histogram histogram{{1, 2}};
108123
// 2 bucket boundaries means there are 3 buckets, so giving just 2 bucket
109124
// increments should result in a length_error.
110125
ASSERT_THROW(histogram.ObserveMultiple({5, 9}, 20), std::length_error);
111126
}
112127

128+
TEST(HistogramTest, observe_multiple_test_length_error2) {
129+
Histogram histogram{{1, 2}};
130+
const std::forward_list<double> values1 = {5, 9};
131+
ASSERT_THROW(histogram.ObserveMultiple(values1.begin(), values1.end(), 20),
132+
std::length_error);
133+
const std::forward_list<double> values2 = {5, 9, 5, 6};
134+
ASSERT_THROW(histogram.ObserveMultiple(values2.begin(), values2.end(), 20),
135+
std::length_error);
136+
}
137+
113138
TEST(HistogramTest, sum_can_go_down) {
114139
Histogram histogram{{1}};
115140
auto metric1 = histogram.Collect();

0 commit comments

Comments
 (0)