91 auto snap = aggregate();
98 p = std::clamp(p, 0.0, 1.0);
101 auto it = snap.percentiles.find(p);
102 if (it != snap.percentiles.end())
108 double target = p *
static_cast<double>(snap.count);
110 for (
size_t i = 0; i < snap.buckets.size(); ++i)
112 if (
static_cast<double>(snap.buckets[i].second) >= target)
114 double lower_bound = (i == 0) ? 0.0 : snap.buckets[i - 1].first;
115 double upper_bound = snap.buckets[i].first;
117 if (std::isinf(upper_bound))
122 uint64_t lower_count = (i == 0) ? 0 : snap.buckets[i - 1].second;
123 uint64_t upper_count = snap.buckets[i].second;
125 if (upper_count == lower_count)
130 double fraction = (target -
static_cast<double>(lower_count))
131 / (
static_cast<double>(upper_count) -
static_cast<double>(lower_count));
133 return lower_bound + (fraction * (upper_bound - lower_bound));
167 auto now = std::chrono::steady_clock::now();
169 if (buckets_.empty())
171 buckets_.push_back(std::make_unique<time_bucket>(config_.hist_config));
172 return *buckets_.back();
175 auto& last = buckets_.back();
176 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - last->start_time);
178 if (elapsed >= bucket_duration_)
180 buckets_.push_back(std::make_unique<time_bucket>(config_.hist_config));
183 return *buckets_.back();
188 std::lock_guard<std::mutex> lock(
mutex_);
194 result.
min_value = std::numeric_limits<double>::infinity();
195 result.
max_value = -std::numeric_limits<double>::infinity();
203 std::vector<std::pair<double, uint64_t>> merged_buckets;
204 bool first_snapshot =
true;
208 auto snap = bucket->hist.snapshot();
209 result.
count += snap.count;
210 result.
sum += snap.sum;
224 merged_buckets = snap.buckets;
225 first_snapshot =
false;
230 std::vector<uint64_t> individual_counts(snap.buckets.size(), 0);
231 for (
size_t i = 0; i < snap.buckets.size(); ++i)
233 uint64_t prev = (i == 0) ? 0 : snap.buckets[i - 1].second;
234 individual_counts[i] = snap.buckets[i].second - prev;
237 std::vector<uint64_t> merged_individual(merged_buckets.size(), 0);
238 for (
size_t i = 0; i < merged_buckets.size(); ++i)
240 uint64_t prev = (i == 0) ? 0 : merged_buckets[i - 1].second;
241 merged_individual[i] = merged_buckets[i].second - prev;
245 for (
size_t i = 0; i < merged_buckets.size() && i < individual_counts.size(); ++i)
247 merged_individual[i] += individual_counts[i];
251 uint64_t cumulative = 0;
252 for (
size_t i = 0; i < merged_buckets.size(); ++i)
254 cumulative += merged_individual[i];
255 merged_buckets[i].second = cumulative;
260 result.
buckets = std::move(merged_buckets);
265 auto calc_percentile = [&](
double p) ->
double
267 double target = p *
static_cast<double>(result.
count);
269 for (
size_t i = 0; i < result.
buckets.size(); ++i)
271 if (
static_cast<double>(result.
buckets[i].second) >= target)
273 double lower_bound = (i == 0) ? 0.0 : result.
buckets[i - 1].first;
274 double upper_bound = result.
buckets[i].first;
276 if (std::isinf(upper_bound))
281 uint64_t lower_count = (i == 0) ? 0 : result.
buckets[i - 1].second;
282 uint64_t upper_count = result.
buckets[i].second;
284 if (upper_count == lower_count)
290 (target -
static_cast<double>(lower_count))
291 / (
static_cast<double>(upper_count) -
static_cast<double>(lower_count));
293 return lower_bound + (fraction * (upper_bound - lower_bound));
303 result.
percentiles[0.999] = calc_percentile(0.999);