blob: 04b08cd4ac2e740124083b606dfce6b409fc72df [file] [log] [blame]
// Copyright 2018 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.
#ifndef UI_LATENCY_WINDOWED_ANALYZER_H_
#define UI_LATENCY_WINDOWED_ANALYZER_H_
#include <cstdint>
#include "base/containers/circular_deque.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "base/trace_event/traced_value.h"
#include "ui/latency/fixed_point.h"
namespace ui {
// FrameRegionResult encodes window of time where a metric was worst.
// The |sample_count| is the number of samples/frames within the time window
// used to calculate the result. It is reported in case the client wants to
// assess the confidence of the result.
struct FrameRegionResult {
double value = 0;
size_t sample_count = 0;
base::TimeTicks window_begin;
base::TimeTicks window_end;
void AsValueInto(base::trace_event::TracedValue* state) const;
};
namespace frame_metrics {
// Client delegates that are specific to each WindowedAnalyzer.
class WindowedAnalyzerClient {
public:
// The WorstMean,RMS,SMR methods will give TransformResult() a chance to
// modify the results via this delegate.
// This can be used to undo any tranformations applied to values added
// to AddSample, such as conversions to fixed point.
virtual double TransformResult(double result) const = 0;
// TODO(brianderson): Replace WindowedAnalyzer::window_queue_ with a client
// interface here. All latency derived metrics should be able to share a
// common history of values. http://crbug.com/822054
};
// Client delegates that can be shared by multiple WindowedAnalyzers.
// Tracks the current window of time that can be stored as the worst
// window of time if a metric detects it as such.
struct SharedWindowedAnalyzerClient {
SharedWindowedAnalyzerClient() : max_window_size(0) {}
explicit SharedWindowedAnalyzerClient(size_t max_window_size)
: max_window_size(max_window_size) {}
SharedWindowedAnalyzerClient(size_t max_window_size,
base::TimeTicks window_begin,
base::TimeTicks window_end)
: max_window_size(max_window_size),
window_begin(window_begin),
window_end(window_end) {}
// Maximum window size in number of samples.
size_t max_window_size;
// Current window of time for the samples being added.
base::TimeTicks window_begin;
base::TimeTicks window_end;
};
// Detects the worst windows of time for a metric.
// Tracks the current values of the current window of time for the
// mean, RMS, and SMR of a single metric. It maintains a history
// of the recent samples and, for each new sample, updates it's accumulators
// using the oldest and newest samples, without looking at any of the other
// samples in between.
class WindowedAnalyzer {
public:
WindowedAnalyzer(const WindowedAnalyzerClient* client,
const SharedWindowedAnalyzerClient* shared_client);
virtual ~WindowedAnalyzer();
// ResetWosrtValues only resets the memory of worst values encountered,
// without resetting recent sample history.
void ResetWorstValues();
// ResetHistory only resets recent sample history without resetting memory
// of the worst values ecnountered.
void ResetHistory();
// Callers of AddSample will already have calculated weighted values to
// track cumulative results, so just let them pass in the values here
// rather than re-calculating them.
void AddSample(uint32_t value,
uint32_t weight,
uint64_t weighted_value,
uint64_t weighted_root,
const Accumulator96b& weighted_square);
// Returns the worst regions encountered so far.
FrameRegionResult ComputeWorstMean() const;
FrameRegionResult ComputeWorstRMS() const;
FrameRegionResult ComputeWorstSMR() const;
protected:
struct QueueEntry {
uint32_t value = 0;
uint32_t weight = 0;
};
// Updates the result with the current value, if it is worse than the
// value in |result| or if |initialize| is true.
template <typename AccumulatorT>
void UpdateWorst(const AccumulatorT& accumulator,
FrameRegionResult* result,
bool initialize) const {
double current_mean = AsDouble(accumulator) / total_weight_;
if (initialize || current_mean > result->value) {
result->value = current_mean;
result->sample_count = window_queue_.size();
result->window_begin = shared_client_->window_begin;
result->window_end = shared_client_->window_end;
}
}
const WindowedAnalyzerClient* const client_;
const SharedWindowedAnalyzerClient* const shared_client_;
// We need to maintain a history of values so we can
// remove old samples from the accumulators.
base::circular_deque<QueueEntry> window_queue_;
uint64_t total_weight_ = 0;
uint64_t accumulator_ = 0;
uint64_t root_accumulator_ = 0;
Accumulator96b square_accumulator_;
// Internal results that track the worst region so far.
// The time region is stored correctly, however the results are intermediate
// and must be adjusted by result_transform_ and fixed_point_multipler before
// exposure to the client. Furthermore, RMS needs to square root the result
// and SMR needs to square the result.
struct InternalResults {
FrameRegionResult mean;
FrameRegionResult root;
FrameRegionResult square;
};
// Optional since they aren't valid until we've seen enough samples.
// This delay prevents the first couple samples from dominating the result.
base::Optional<InternalResults> results_;
DISALLOW_COPY_AND_ASSIGN(WindowedAnalyzer);
};
} // namespace frame_metrics
} // namespace ui
#endif // UI_LATENCY_WINDOWED_ANALYZER_H_