blob: 8a7eb63fe326ffb6152c16bfe7ec8d5331e7f292 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "histogram.h"
#include <sstream>
#include <cmath>
#define LOG_TAG "TuningFork"
#include "Log.h"
#include "clearcutserializer.h"
namespace tuningfork {
Histogram::Histogram(float start_ms, float end_ms, int num_buckets_between)
: start_ms_(start_ms), end_ms_(end_ms),
bucket_dt_ms_((end_ms_ - start_ms_) / num_buckets_between),
num_buckets_(num_buckets_between + 2),
buckets_(num_buckets_), auto_range_(start_ms_ == 0 && end_ms_ == 0), count_(0) {
std::fill(buckets_.begin(), buckets_.end(), 0);
if (auto_range_)
samples_.reserve(num_buckets_);
else if (bucket_dt_ms_ <= 0)
ALOGE("Histogram end needs to be larger than histogram begin");
}
Histogram::Histogram(const TFHistogram &hs)
: Histogram(hs.bucket_min, hs.bucket_max, hs.n_buckets) {
}
void Histogram::Add(Sample dt_ms) {
if (auto_range_) {
samples_.push_back(dt_ms);
if (samples_.size() == samples_.capacity()) {
CalcBucketsFromSamples();
}
} else {
int i = (dt_ms - start_ms_) / bucket_dt_ms_;
if (i < 0)
buckets_[0]++;
else if (i + 1 >= num_buckets_)
buckets_[num_buckets_ - 1]++;
else
buckets_[i + 1]++;
++count_;
}
}
void Histogram::CalcBucketsFromSamples() {
Sample min_dt = std::numeric_limits<Sample>::max();
Sample max_dt = std::numeric_limits<Sample>::min();
Sample sum = 0;
Sample sum2 = 0;
for (Sample d: samples_) {
if (d < min_dt) min_dt = d;
if (d > max_dt) max_dt = d;
sum += d;
sum2 += d * d;
}
size_t n = samples_.size();
Sample mean = sum / n;
Sample var = sum2 / n - mean * mean;
if (var < 0) var = 0; // Can be negative due to rounding errors
Sample stddev = sqrt(var);
start_ms_ = std::max(mean - kAutoSizeNumStdDev * stddev, 0.0);
end_ms_ = mean + kAutoSizeNumStdDev * stddev;
bucket_dt_ms_ = (end_ms_ - start_ms_) / (num_buckets_ - 2);
if (bucket_dt_ms_ < kAutoSizeMinBucketSizeMs) {
bucket_dt_ms_ = kAutoSizeMinBucketSizeMs;
Sample w = bucket_dt_ms_ * (num_buckets_ - 2);
start_ms_ = mean - w / 2;
end_ms_ = mean + w / 2;
}
auto_range_ = false;
for (Sample d: samples_) {
Add(d);
}
}
std::string Histogram::ToJSON() const {
std::stringstream str;
str.precision(2);
str << std::fixed;
if (auto_range_) {
str << "{\"pmax\":[],\"cnts\":[]}";
} else {
str << "{\"pmax\":[";
Sample x = start_ms_;
for (int i = 0; i < num_buckets_ - 1; ++i) {
str << x << ",";
x += bucket_dt_ms_;
}
str << "99999],\"cnts\":[";
for (int i = 0; i < num_buckets_ - 1; ++i) {
str << buckets_[i] << ",";
}
if (num_buckets_ > 0)
str << buckets_.back();
str << "]}";
}
return str.str();
}
void Histogram::Clear(bool autorange) {
std::fill(buckets_.begin(), buckets_.end(), 0);
samples_.clear();
auto_range_ = autorange;
count_ = 0;
}
} // namespace tuningfork