| <!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> |