// 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 #include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/optional.h" #include "base/time/time.h" #include "base/trace_event/trace_event_argument.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; void AsValueInto(base::trace_event::TracedValue* state) 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 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 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 results_; DISALLOW_COPY_AND_ASSIGN(WindowedAnalyzer); }; } // namespace frame_metrics } // namespace ui #endif // UI_LATENCY_WINDOWED_ANALYZER_H_