Index: tracing/tracing/metrics/system_health/hazard_metric.html |
diff --git a/tracing/tracing/metrics/system_health/hazard_metric.html b/tracing/tracing/metrics/system_health/hazard_metric.html |
index 9ee9103b73ab37b3aa6015f1e6a8959e03baca1d..9696177978227c8a810640ed231fec345f1834a1 100644 |
--- a/tracing/tracing/metrics/system_health/hazard_metric.html |
+++ b/tracing/tracing/metrics/system_health/hazard_metric.html |
@@ -6,9 +6,7 @@ found in the LICENSE file. |
--> |
<link rel="import" href="/tracing/metrics/metric_registry.html"> |
-<link rel="import" |
- href="/tracing/metrics/system_health/responsiveness_metric.html"> |
-<link rel="import" href="/tracing/model/user_model/idle_expectation.html"> |
+<link rel="import" href="/tracing/metrics/system_health/long_tasks_metric.html"> |
<link rel="import" href="/tracing/value/numeric.html"> |
<link rel="import" href="/tracing/value/value.html"> |
@@ -16,67 +14,41 @@ found in the LICENSE file. |
'use strict'; |
tr.exportTo('tr.metrics.sh', function() { |
- var LONG_TASK_MS = 50; |
- |
var normalizedPercentage_smallerIsBetter = |
tr.v.Unit.byName.normalizedPercentage_smallerIsBetter; |
- var timeDurationInMs_smallerIsBetter = |
- tr.v.Unit.byName.timeDurationInMs_smallerIsBetter; |
- |
- var FAST_RESPONSE_HISTOGRAM = tr.v.Numeric.fromDict({ |
- unit: 'unitless', |
- min: 66, |
- max: 2200, |
- centralBinWidth: 214, |
- underflowBin: {min: -Number.MAX_VALUE, max: 66, count: 1000}, |
- centralBins: [ |
- {min: 66, max: 280, count: 708}, |
- {min: 280, max: 493, count: 223}, |
- {min: 493, max: 706, count: 50}, |
- {min: 706, max: 920, count: 33}, |
- {min: 920, max: 1133, count: 23}, |
- {min: 1133, max: 1346, count: 17}, |
- {min: 1346, max: 1560, count: 12}, |
- {min: 1560, max: 1773, count: 8}, |
- {min: 1773, max: 1987, count: 4}, |
- {min: 1987, max: 2200, count: 1} |
- ], |
- overflowBin: {min: 2200, max: Number.MAX_VALUE, count: 0} |
- }); |
- |
- function computeDurationResponsiveness(histogram, duration) { |
- return histogram.getInterpolatedCountAt(duration) / histogram.maxCount; |
- } |
- |
- function findLongTasks(ue) { |
- var longTasks = []; |
- // NB: This misses tasks that were associated with another UE, |
- // since only unassociated events are vacuumed up into IdleExpectations. |
- ue.associatedEvents.forEach(function(event) { |
- if ((event instanceof tr.model.ThreadSlice) && |
- (event.duration > LONG_TASK_MS) && |
- event.isTopLevel) |
- longTasks.push(event); |
- }); |
- return longTasks; |
- } |
+ // 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 idle task. |
- // It is at least DEFAULT_LONG_TASK_MS. |
- // The FAST_RESPONSE_HISTOGRAM was designed to permit both a 50ms idle task |
- // when a Scroll Response begins, plus 16ms latency between the idle task |
+ // 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 idle task exceeds DEFAULT_LONG_TASK_MS. |
+ // that the long task exceeds LONG_TASK_MS. |
durationMs += 16; |
- // computeDurationResponsiveness returns a normalized percentage that |
+ // 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 |
@@ -89,79 +61,85 @@ tr.exportTo('tr.metrics.sh', function() { |
// 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 1 - computeDurationResponsiveness( |
- FAST_RESPONSE_HISTOGRAM, durationMs); |
+ 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 "stability".) |
- // Also, that version assumes that the 'ary' will be UserExpectations, whereas |
- // this version assumes that the 'ary' will be scores. |
+ /** |
+ * 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); |
} |
- // This metric requires only the 'toplevel' tracing category, |
- // in addition to whatever categories are required to compute the |
- // IdleExpectations (or rather the R/A/Ls that delineate the Idles). |
- // This metric computes a hazard score for each Idle independently (instead of |
- // grouping all long idle tasks together) because each Idle is different: |
- // the Idle before a Load is usually very empty, whereas the Idle immediately |
- // after a Load is usually still very active, since Loads end at a very early |
- // end point (the first contentful paint) while many parts of the page are |
- // still loading. (There may not necessarily be an Idle after a Load in |
- // real-world traces, but there almost always is in telemetry.) |
- function computeLongIdleTaskHazard(hazardScores, ue) { |
- var longTaskScores = []; |
- |
- findLongTasks(ue).forEach(function(longTask) { |
- longTaskScores.push(computeResponsivenessRisk(longTask.duration)); |
+ /** |
+ * 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)); |
}); |
- |
- var options = {description: 'Risk of impacting responsiveness'}; |
- |
- var hazardScore = tr.b.Statistics.weightedMean( |
- longTaskScores, perceptualBlendSmallerIsBetter); |
- |
- if (hazardScore === undefined) |
- hazardScore = 0; |
- |
- hazardScores.push(hazardScore); |
+ return tr.b.Statistics.weightedMean( |
+ taskHazardScores, perceptualBlendSmallerIsBetter); |
} |
- function hazardMetric(values, model) { |
- var hazardScores = []; |
- |
- model.userModel.expectations.forEach(function(ue) { |
- // Add normalized metrics to diagnostics.values. |
- // TODO(memory): Add memory here. |
- |
- if (ue instanceof tr.model.um.IdleExpectation) |
- computeLongIdleTaskHazard(hazardScores, ue); |
+ /** |
+ * 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); |
+ } |
- var options = {description: 'Risk of impacting responsiveness'}; |
- |
- var overallHazard = tr.b.Statistics.weightedMean( |
- hazardScores, 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; |
values.addValue(new tr.v.NumericValue( |
'hazard', new tr.v.ScalarNumeric( |
- normalizedPercentage_smallerIsBetter, overallHazard), |
- options)); |
+ normalizedPercentage_smallerIsBetter, overallHazard))); |
} |
tr.metrics.MetricRegistry.register(hazardMetric); |
return { |
hazardMetric: hazardMetric, |
- computeLongIdleTaskHazard: computeLongIdleTaskHazard |
+ computeHazardForLongTasksInRangeOnThread: |
+ computeHazardForLongTasksInRangeOnThread, |
+ computeHazardForLongTasks: computeHazardForLongTasks, |
+ computeResponsivenessRisk: computeResponsivenessRisk |
}; |
}); |
</script> |