1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/metrics/latency_ukm_reporter.h"
10 #include "base/rand_util.h"
11 #include "cc/trees/ukm_manager.h"
15 // We use a Poisson process with an exponential decay multiplier. The goal is to
16 // get many randomly distributed samples early during page load and initial
17 // interaction, then samples at an exponentially decreasing rate to effectively
18 // cap the number of samples. The particular parameters chosen here give roughly
19 // 5-10 samples in the first 100 frames, decaying to several hours between
20 // samples by the 40th sample. The multiplier value should be tuned to achieve a
21 // total sample count that avoids throttling by the UKM system.
22 class LatencyUkmReporter::SamplingController {
24 SamplingController() = default;
25 ~SamplingController() = default;
27 // When a new UKM event is issued, this function should be called (once and
28 // only once) by the client to determine whether that event should be recorded
29 // or ignored, according to the sampling parameters. The sampling state will
30 // be updated to be ready for the next UKM event.
31 bool ShouldRecordNextEvent() {
32 bool should_record = false;
33 if (!frames_to_next_event_) {
35 frames_to_next_event_ = SampleFramesToNextEvent();
37 DCHECK_GT(frames_to_next_event_, 0);
38 --frames_to_next_event_;
43 // The |kSampleRateMultiplier| and |kSampleDecayRate| have been set to meet
44 // UKM goals for data volume.
45 const double kSampleDecayRate = 1.0;
46 const double kSampleRateMultiplier = 2.0;
48 int SampleFramesToNextEvent() {
49 // Sample from an exponential distribution to give a Poisson distribution
50 // of samples per time unit, then weigh it with an exponential multiplier
51 // to give a few samples in rapid succession (for frames early in the
52 // page's life) then exponentially fewer as the page lives longer.
53 // RandDouble() returns [0,1), but we need (0,1]. If RandDouble() is
54 // uniformly random, so is 1-RandDouble(), so use it to adjust the range.
55 // When RandDouble() returns 0.0, as it could, we will get a float_sample
56 // of 0, causing underflow. So rejection sample until we get a positive
58 double float_sample = 0.0;
60 float_sample = -(kSampleRateMultiplier *
61 std::exp(samples_so_far_ * kSampleDecayRate) *
62 std::log(1.0 - base::RandDouble()));
63 } while (float_sample == 0.0);
64 // float_sample is positive, so we don't need to worry about underflow.
65 // After around 30 samples we will end up with a super high sample. That's
66 // OK because it just means we'll stop reporting metrics for that session,
67 // but we do need to be careful about overflow and NaN.
70 std::isnan(float_sample)
72 : base::saturated_cast<int>(std::ceil(float_sample));
73 return integer_sample;
76 int samples_so_far_ = 0;
77 int frames_to_next_event_ = 0;
80 LatencyUkmReporter::LatencyUkmReporter()
81 : compositor_latency_sampling_controller_(
82 std::make_unique<SamplingController>()),
83 event_latency_sampling_controller_(
84 std::make_unique<SamplingController>()) {}
86 LatencyUkmReporter::~LatencyUkmReporter() = default;
88 void LatencyUkmReporter::ReportCompositorLatencyUkm(
89 const CompositorFrameReporter::FrameReportTypes& report_types,
90 const std::vector<CompositorFrameReporter::StageData>& stage_history,
91 const ActiveTrackers& active_trackers,
92 const CompositorFrameReporter::ProcessedBlinkBreakdown&
93 processed_blink_breakdown,
94 const CompositorFrameReporter::ProcessedVizBreakdown&
95 processed_viz_breakdown) {
97 compositor_latency_sampling_controller_->ShouldRecordNextEvent()) {
98 ukm_manager_->RecordCompositorLatencyUKM(
99 report_types, stage_history, active_trackers, processed_blink_breakdown,
100 processed_viz_breakdown);
104 void LatencyUkmReporter::ReportEventLatencyUkm(
105 const EventMetrics::List& events_metrics,
106 const std::vector<CompositorFrameReporter::StageData>& stage_history,
107 const CompositorFrameReporter::ProcessedBlinkBreakdown&
108 processed_blink_breakdown,
109 const CompositorFrameReporter::ProcessedVizBreakdown&
110 processed_viz_breakdown) {
112 event_latency_sampling_controller_->ShouldRecordNextEvent()) {
113 ukm_manager_->RecordEventLatencyUKM(events_metrics, stage_history,
114 processed_blink_breakdown,
115 processed_viz_breakdown);