blob: 70201ef5957a47242101ac585adaee120f625689 [file] [log] [blame]
// 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 "cc/metrics/latency_ukm_reporter.h"
#include <climits>
#include <memory>
#include "base/rand_util.h"
#include "cc/trees/ukm_manager.h"
namespace cc {
// We use a Poisson process with an exponential decay multiplier. The goal is to
// get many randomly distributed samples early during page load and initial
// interaction, then samples at an exponentially decreasing rate to effectively
// cap the number of samples. The particular parameters chosen here give roughly
// 5-10 samples in the first 100 frames, decaying to several hours between
// samples by the 40th sample. The multiplier value should be tuned to achieve a
// total sample count that avoids throttling by the UKM system.
class LatencyUkmReporter::SamplingController {
public:
SamplingController() = default;
~SamplingController() = default;
// When a new UKM event is issued, this function should be called (once and
// only once) by the client to determine whether that event should be recorded
// or ignored, according to the sampling parameters. The sampling state will
// be updated to be ready for the next UKM event.
bool ShouldRecordNextEvent() {
bool should_record = false;
if (!frames_to_next_event_) {
should_record = true;
frames_to_next_event_ = SampleFramesToNextEvent();
}
DCHECK_GT(frames_to_next_event_, 0);
--frames_to_next_event_;
return should_record;
}
private:
// The |kSampleRateMultiplier| and |kSampleDecayRate| have been set to meet
// UKM goals for data volume.
const double kSampleDecayRate = 1.0;
const double kSampleRateMultiplier = 2.0;
int SampleFramesToNextEvent() {
// Sample from an exponential distribution to give a Poisson distribution
// of samples per time unit, then weigh it with an exponential multiplier
// to give a few samples in rapid succession (for frames early in the
// page's life) then exponentially fewer as the page lives longer.
// RandDouble() returns [0,1), but we need (0,1]. If RandDouble() is
// uniformly random, so is 1-RandDouble(), so use it to adjust the range.
// When RandDouble() returns 0.0, as it could, we will get a float_sample
// of 0, causing underflow. So rejection sample until we get a positive
// count.
double float_sample = 0.0;
do {
float_sample = -(kSampleRateMultiplier *
std::exp(samples_so_far_ * kSampleDecayRate) *
std::log(1.0 - base::RandDouble()));
} while (float_sample == 0.0);
// float_sample is positive, so we don't need to worry about underflow.
// After around 30 samples we will end up with a super high sample. That's
// OK because it just means we'll stop reporting metrics for that session,
// but we do need to be careful about overflow and NaN.
samples_so_far_++;
int integer_sample =
std::isnan(float_sample)
? INT_MAX
: base::saturated_cast<int>(std::ceil(float_sample));
return integer_sample;
}
int samples_so_far_ = 0;
int frames_to_next_event_ = 0;
};
LatencyUkmReporter::LatencyUkmReporter()
: compositor_latency_sampling_controller_(
std::make_unique<SamplingController>()),
event_latency_sampling_controller_(
std::make_unique<SamplingController>()) {}
LatencyUkmReporter::~LatencyUkmReporter() = default;
void LatencyUkmReporter::ReportCompositorLatencyUkm(
const CompositorFrameReporter::FrameReportTypes& report_types,
const std::vector<CompositorFrameReporter::StageData>& stage_history,
const ActiveTrackers& active_trackers,
const CompositorFrameReporter::ProcessedBlinkBreakdown&
processed_blink_breakdown,
const CompositorFrameReporter::ProcessedVizBreakdown&
processed_viz_breakdown) {
if (ukm_manager_ &&
compositor_latency_sampling_controller_->ShouldRecordNextEvent()) {
ukm_manager_->RecordCompositorLatencyUKM(
report_types, stage_history, active_trackers, processed_blink_breakdown,
processed_viz_breakdown);
}
}
void LatencyUkmReporter::ReportEventLatencyUkm(
const EventMetrics::List& events_metrics,
const std::vector<CompositorFrameReporter::StageData>& stage_history,
const CompositorFrameReporter::ProcessedBlinkBreakdown&
processed_blink_breakdown,
const CompositorFrameReporter::ProcessedVizBreakdown&
processed_viz_breakdown) {
if (ukm_manager_ &&
event_latency_sampling_controller_->ShouldRecordNextEvent()) {
ukm_manager_->RecordEventLatencyUKM(events_metrics, stage_history,
processed_blink_breakdown,
processed_viz_breakdown);
}
}
} // namespace cc