blob: e8b6eeb2ba81ccfa693615e7ebdd9e0ab05bd405 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2015 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.
-->
<link rel="import" href="/tracing/metrics/metric_registry.html">
<link rel="import" href="/tracing/metrics/system_health/long_tasks_metric.html">
<link rel="import" href="/tracing/value/numeric.html">
<script>
'use strict';
tr.exportTo('tr.metrics.sh', function() {
// The following math is easier if the units are seconds rather than ms,
// so durations will be converted from ms to s.
var MS_PER_S = 1000;
// https://www.desmos.com/calculator/ysabhcc42g
var RESPONSE_RISK =
tr.b.Statistics.LogNormalDistribution.fromMedianAndDiminishingReturns(
100 / MS_PER_S, 50 / MS_PER_S);
/**
* This helper function computes the risk that a task of the given duration
* would impact the responsiveness of a speculative input.
*
* @param {number} durationMs
* @return {number} task hazard
*/
function computeResponsivenessRisk(durationMs) {
// Returns 0 when the risk of impacting responsiveness is minimal.
// Returns 1 when it is maximal.
// durationMs is the duration of a long task.
// It is at least LONG_TASK_MS.
// The FAST_RESPONSE_HISTOGRAM was designed to permit both a 50ms task
// when a Scroll Response begins, plus 16ms latency between the task
// and the first frame of the scroll, without impacting the responsiveness
// score.
// Add 16ms to durationMs to simulate the standard (maximum ideal) scroll
// response latency, and use the FAST_RESPONSE_HISTOGRAM to punish every ms
// that the long task exceeds LONG_TASK_MS.
durationMs += 16;
// This returns a normalized percentage that
// represents the fraction of users that would be satisfied with a
// Scroll Response that takes durationMs to respond.
// The risk of impacting responsiveness is approximated as the long task's
// impact on a hypothetical Scroll Response that starts when the long task
// starts, and then takes the standard 16ms to respond after the long task
// finishes.
// We imagine a Scroll Response instead of a Load or another type of
// Response because the Scroll Response carries the strictest expectation.
// The risk of impacting responsiveness is framed as the fraction of users
// that would be *un*satisifed with the responsiveness of that hypothetical
// Scroll Response. The fraction of users who are unsatisfied with something
// is equal to 1 - the fraction of users who are satisfied with it.
return RESPONSE_RISK.computePercentile(durationMs / MS_PER_S);
}
/**
* This weighting function is similar to tr.metrics.sh.perceptualBlend,
* but this version is appropriate for SmallerIsBetter metrics, whereas
* that version is for BiggerIsBetter metrics.
* (This would not be necessary if hazard were reframed as a BiggerIsBetter
* metric such as "input readiness".)
* Also, that version assumes that the 'ary' will be UserExpectations, whereas
* this version assumes that the 'ary' will be scores.
*
* @param {number} hazardScore
* @return {number} weight
*/
function perceptualBlendSmallerIsBetter(hazardScore) {
return Math.exp(hazardScore);
}
/**
* Compute and return the normalized score for the risk that a speculative
* input's responsiveness would have been impacted by long tasks on the given
* thread in the given range.
*
* @param {tr.model.Thread} thread
* @param {tr.b.Range=} opt_range
* @return {number} hazard
*/
function computeHazardForLongTasksInRangeOnThread(thread, opt_range) {
var taskHazardScores = [];
tr.metrics.sh.iterateLongTopLevelTasksOnThreadInRange(
thread, opt_range, function(task) {
taskHazardScores.push(computeResponsivenessRisk(task.duration));
});
return tr.b.Statistics.weightedMean(
taskHazardScores, perceptualBlendSmallerIsBetter);
}
/**
* Compute and return the normalized score for the risk that a speculative
* input's responsiveness would have been impacted by long tasks.
*
* @param {tr.model.Model} model The model.
* @return {number} hazard
*/
function computeHazardForLongTasks(model) {
var threadHazardScores = [];
tr.metrics.sh.iterateRendererMainThreads(model, function(thread) {
threadHazardScores.push(computeHazardForLongTasksInRangeOnThread(
thread));
});
return tr.b.Statistics.weightedMean(
threadHazardScores, perceptualBlendSmallerIsBetter);
}
/**
* This EXPERIMENTAL metric computes a scalar normalized score that
* represents the risk that a speculative input's responsiveness would have
* been impacted by long tasks.
* This metric requires only the 'toplevel' tracing category.
*/
function hazardMetric(values, model) {
var overallHazard = computeHazardForLongTasks(model);
if (overallHazard === undefined)
overallHazard = 0;
var hist = new tr.v.Histogram('hazard',
tr.b.Unit.byName.normalizedPercentage_smallerIsBetter);
hist.addSample(overallHazard);
values.addHistogram(hist);
}
tr.metrics.MetricRegistry.register(hazardMetric);
return {
hazardMetric: hazardMetric,
computeHazardForLongTasksInRangeOnThread:
computeHazardForLongTasksInRangeOnThread,
computeHazardForLongTasks: computeHazardForLongTasks,
computeResponsivenessRisk: computeResponsivenessRisk
};
});
</script>