blob: 3c3ec422ea4b9328c6ecfd2c1f6842fb54683778 [file] [edit]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tracing/tracing/value/histogram.h"
#include <cassert>
#include <cmath>
#include <cstdint>
#include <map>
#include <random>
#include "tracing/tracing/value/running_statistics.h"
namespace catapult {
namespace proto = tracing::tracing::proto;
static constexpr std::pair<const char*, proto::Unit> kJsonUnitToProtoUnit[] = {
{"ms", proto::MS},
{"msBestFitFormat", proto::MS_BEST_FIT_FORMAT},
{"tsMs", proto::TS_MS},
{"n%", proto::N_PERCENT},
{"sizeInBytes", proto::SIZE_IN_BYTES},
{"bytesPerSecond", proto::BYTES_PER_SECOND},
{"J", proto::J},
{"W", proto::W},
{"A", proto::A},
{"Ah", proto::AH},
{"V", proto::V},
{"Hz", proto::HERTZ},
{"unitless", proto::UNITLESS},
{"count", proto::COUNT},
{"sigma", proto::SIGMA}};
// Assume a single bin. The default num sample values is num bins * 10.
static constexpr int kDefaultNumSampleValues = 10;
class HistogramBuilder::Resampler {
public:
Resampler() : distribution_(0.0, 1.0) {}
// When processing a stream of samples, call this method for each new sample
// in order to decide whether to keep it in |samples|.
// Modifies |samples| in-place such that its length never exceeds
// |max_num_samples|. After |stream_length| samples have been processed, each
// sample has equal probability of being retained in |samples|. The order of
// samples is not preserved after |stream_length| exceeds |num_samples|.
void UniformlySampleStream(std::vector<double>* samples,
uint32_t stream_length,
double new_element,
uint32_t max_num_samples) {
assert(max_num_samples > 0);
if (stream_length <= max_num_samples) {
if (samples->size() >= stream_length) {
(*samples)[stream_length - 1] = new_element;
} else {
samples->push_back(new_element);
}
return;
}
double prob_keep = static_cast<double>(max_num_samples) / stream_length;
if (random() > prob_keep) {
// Reject new sample.
return;
}
// Replace a random element.
int victim = static_cast<int>(std::floor(random() * max_num_samples));
(*samples)[victim] = new_element;
}
private:
double random() { return distribution_(generator_); }
std::default_random_engine generator_;
std::uniform_real_distribution<double> distribution_;
};
HistogramBuilder::HistogramBuilder(
const std::string& name, proto::UnitAndDirection unit)
: resampler_(std::make_unique<Resampler>()),
running_statistics_(std::make_unique<RunningStatistics>()),
name_(name),
unit_(unit),
num_nans_(0) {
max_num_sample_values_ = kDefaultNumSampleValues;
}
HistogramBuilder::~HistogramBuilder() = default;
void HistogramBuilder::AddSample(double value) {
if (std::isnan(value)) {
num_nans_++;
} else {
running_statistics_->Add(value);
int num_values = running_statistics_->count();
resampler_->UniformlySampleStream(&sample_values_, num_nans_ + num_values,
value, max_num_sample_values_);
}
}
void HistogramBuilder::AddDiagnostic(
const std::string& key,
tracing::tracing::proto::Diagnostic diagnostic) {
diagnostics_[key] = diagnostic;
}
void HistogramBuilder::SetSummaryOptions(proto::SummaryOptions options) {
options_ = options;
}
std::unique_ptr<proto::Histogram> HistogramBuilder::toProto() const {
auto histogram = std::make_unique<proto::Histogram>();
histogram->set_name(name_);
*histogram->mutable_unit() = unit_;
histogram->set_description(description_);
proto::DiagnosticMap* diagnostics = histogram->mutable_diagnostics();
for (const auto& pair : diagnostics_) {
auto* diagnostic_map = diagnostics->mutable_diagnostic_map();
(*diagnostic_map)[pair.first] = pair.second;
}
for (double sample: sample_values_) {
histogram->add_sample_values(sample);
}
histogram->set_max_num_sample_values(max_num_sample_values_);
histogram->set_num_nans(num_nans_);
proto::RunningStatistics* running = histogram->mutable_running();
running->set_count(running_statistics_->count());
running->set_max(running_statistics_->max());
if (running_statistics_->meanlogs_valid()) {
running->set_meanlogs(running_statistics_->meanlogs());
}
running->set_mean(running_statistics_->mean());
running->set_min(running_statistics_->min());
running->set_sum(running_statistics_->sum());
running->set_variance(running_statistics_->variance());
proto::SummaryOptions* options = histogram->mutable_summary_options();
*options = options_;
return histogram;
}
tracing::tracing::proto::Unit UnitFromJsonUnit(std::string unit) {
unit.erase(std::find(unit.begin(), unit.end(), '_'), unit.end());
for (const auto& pair : kJsonUnitToProtoUnit) {
if (unit == pair.first) {
return pair.second;
}
}
return proto::UNITLESS;
}
} // namespace catapult