blob: 81dd774bc3390339469c57891f9f1266347f89bf [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. All Rights Reserved.
//
// 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.
#ifndef COBALT_BASE_C_VAL_TIME_INTERVAL_ENTRY_STATS_H_
#define COBALT_BASE_C_VAL_TIME_INTERVAL_ENTRY_STATS_H_
#include <algorithm>
#include <cmath>
#include <string>
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "cobalt/base/c_val.h"
namespace base {
namespace CValDetail {
// This class tracks entries over a specified time period, which it does not
// retain in memory. When the time period has elapsed, the average, minimum,
// maximum, and standard deviation of that time period are recorded with CVals,
// and the tracking resets for the next time period.
// NOTE: In cases where the number of entries are small and stats for time
// periods are not needed, |CValCollectionEntryStats| may be more
// appropriate. It retains the entries for the collection in memory so
// that it can provide the collection's percentiles.
template <typename EntryType, typename Visibility>
class CValTimeIntervalEntryStatsImpl {
public:
CValTimeIntervalEntryStatsImpl(const std::string& name,
int64 time_interval_in_ms);
void AddEntry(const EntryType& value);
void AddEntry(const EntryType& value, const base::TimeTicks& now);
private:
void AddToActiveEntryStats(const EntryType& value);
void ResetActiveEntryStats();
const int64 time_interval_in_ms_;
// CVals of the stats for the previously completed time interval.
base::CVal<size_t, Visibility> count_;
base::CVal<EntryType, Visibility> average_;
base::CVal<EntryType, Visibility> minimum_;
base::CVal<EntryType, Visibility> maximum_;
base::CVal<EntryType, Visibility> standard_deviation_;
// Active time interval-related
base::TimeTicks active_start_time_;
// Variance calculations are based upon the following:
// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Computing_shifted_data
// Each value is shifted by an estimated mean when calculating the variance.
// This increases accuracy in cases where the variance is small but the values
// involved are extremely large.
// The following article provides a more detailed discussion on the topic:
// http://www.cs.yale.edu/publications/techreports/tr222.pdf
// During the first time interval, the initial entry acts as the estimated
// mean. All subsequent loops use the mean of the previous time interval.
double active_estimated_mean_;
// Stats updated with each new entry.
int64 active_count_;
double active_shifted_sum_;
double active_shifted_sum_squares_;
EntryType active_minimum_;
EntryType active_maximum_;
};
template <typename EntryType, typename Visibility>
CValTimeIntervalEntryStatsImpl<EntryType, Visibility>::
CValTimeIntervalEntryStatsImpl(const std::string& name,
int64 time_interval_in_ms)
: time_interval_in_ms_(time_interval_in_ms),
count_(base::StringPrintf("%s.Cnt", name.c_str()), 0, "Total entries."),
average_(base::StringPrintf("%s.Avg", name.c_str()), EntryType(),
"Average time."),
minimum_(base::StringPrintf("%s.Min", name.c_str()), EntryType(),
"Minimum time."),
maximum_(base::StringPrintf("%s.Max", name.c_str()), EntryType(),
"Maximum time."),
standard_deviation_(base::StringPrintf("%s.Std", name.c_str()),
EntryType(), "Standard deviation of times."),
active_estimated_mean_(0) {
ResetActiveEntryStats();
}
template <typename EntryType, typename Visibility>
void CValTimeIntervalEntryStatsImpl<EntryType, Visibility>::AddEntry(
const EntryType& value) {
base::TimeTicks now = base::TimeTicks::Now();
AddEntry(value, now);
}
template <typename EntryType, typename Visibility>
void CValTimeIntervalEntryStatsImpl<EntryType, Visibility>::AddEntry(
const EntryType& value, const base::TimeTicks& now) {
// Check to see if the timer hasn't started yet. This happens when this is
// the first entry ever added. In this case, the timer is simply started and
// the estimated mean is set to the passed in value.
if (active_start_time_.is_null()) {
active_start_time_ = now;
active_estimated_mean_ = CValDetail::ToDouble(value);
// Otherwise, check for the time interval having ended. If it has, then the
// CVals are updated using the active stats, the active stats are reset, and
// the timer restarted.
} else if ((now - active_start_time_).InMilliseconds() >
time_interval_in_ms_) {
// The active count can never be 0 when a time interval has ended. There
// must always be at least one entry when the time is non-null.
DCHECK_GT(active_count_, 0);
count_ = active_count_;
double active_shifted_mean = active_shifted_sum_ / active_count_;
average_ = CValDetail::FromDouble<EntryType>(active_estimated_mean_ +
active_shifted_mean);
// The equation comes from the following:
// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Computing_shifted_data
double variance =
(active_shifted_sum_squares_ -
((active_shifted_sum_ * active_shifted_sum_) / active_count_)) /
active_count_;
variance = std::max(variance, 0.0);
standard_deviation_ =
CValDetail::FromDouble<EntryType>(std::sqrt(variance));
minimum_ = active_minimum_;
maximum_ = active_maximum_;
// Prepare the active data for the next time interval, including updating
// the estimated mean with the calculated mean of the previous time
// interval.
active_start_time_ = now;
active_estimated_mean_ += active_shifted_mean;
ResetActiveEntryStats();
}
AddToActiveEntryStats(value);
}
template <typename EntryType, typename Visibility>
void CValTimeIntervalEntryStatsImpl<
EntryType, Visibility>::AddToActiveEntryStats(const EntryType& value) {
++active_count_;
double shifted_value_as_double =
CValDetail::ToDouble(value) - active_estimated_mean_;
active_shifted_sum_ += shifted_value_as_double;
active_shifted_sum_squares_ +=
shifted_value_as_double * shifted_value_as_double;
if (value < active_minimum_) {
active_minimum_ = value;
}
if (value > active_maximum_) {
active_maximum_ = value;
}
}
template <typename EntryType, typename Visibility>
void CValTimeIntervalEntryStatsImpl<EntryType,
Visibility>::ResetActiveEntryStats() {
active_count_ = 0;
active_shifted_sum_ = 0;
active_shifted_sum_squares_ = 0;
active_minimum_ = CValDetail::Max<EntryType>();
active_maximum_ = CValDetail::Min<EntryType>();
}
#if !defined(ENABLE_DEBUG_C_VAL)
// This is a stub class that disables CValTimeIntervalEntryStats when
// ENABLE_DEBUG_C_VAL is not defined.
template <typename EntryType>
class CValTimeIntervalEntryStatsStub {
public:
CValTimeIntervalEntryStatsStub(const std::string& name,
int64 time_interval_in_ms) {
}
void AddEntry(const EntryType& value) {}
void AddEntry(const EntryType& value, const base::TimeTicks& now) {}
};
#endif // ENABLE_DEBUG_C_VAL
} // namespace CValDetail
template <typename EntryType, typename Visibility = CValDebug>
class CValTimeIntervalEntryStats {};
template <typename EntryType>
#if defined(ENABLE_DEBUG_C_VAL)
// When ENABLE_DEBUG_C_VAL is defined, CVals with Visibility set to CValDebug
// are tracked through the CVal system.
class CValTimeIntervalEntryStats<EntryType, CValDebug>
: public CValDetail::CValTimeIntervalEntryStatsImpl<EntryType, CValDebug> {
typedef CValDetail::CValTimeIntervalEntryStatsImpl<EntryType, CValDebug>
CValParent;
#else // ENABLE_DEBUG_C_VAL
// When ENABLE_DEBUG_C_VAL is not defined, CVals with Visibility set to
// CValDebug are not tracked though the CVal system and
// CValTimeIntervalEntryStats can be stubbed out.
class CValTimeIntervalEntryStats<EntryType, CValDebug>
: public CValDetail::CValTimeIntervalEntryStatsStub<EntryType> {
typedef CValDetail::CValTimeIntervalEntryStatsStub<EntryType> CValParent;
#endif // ENABLE_DEBUG_C_VAL
public:
CValTimeIntervalEntryStats(const std::string& name, int64 time_interval_in_ms)
: CValParent(name, time_interval_in_ms) {}
};
// CVals with visibility set to CValPublic are always tracked though the CVal
// system, regardless of the ENABLE_DEBUG_C_VAL state.
template <typename EntryType>
class CValTimeIntervalEntryStats<EntryType, CValPublic>
: public CValDetail::CValTimeIntervalEntryStatsImpl<EntryType, CValPublic> {
typedef CValDetail::CValTimeIntervalEntryStatsImpl<EntryType, CValPublic>
CValParent;
public:
CValTimeIntervalEntryStats(const std::string& name, int64 time_interval_in_ms)
: CValParent(name, time_interval_in_ms) {}
};
} // namespace base
#endif // COBALT_BASE_C_VAL_TIME_INTERVAL_ENTRY_STATS_H_