Index: tracing/tracing/metrics/estimated_input_latency_metric.html |
diff --git a/tracing/tracing/metrics/estimated_input_latency_metric.html b/tracing/tracing/metrics/estimated_input_latency_metric.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..23e2160ed82516eeaae5b60f444fa277e4cf01b0 |
--- /dev/null |
+++ b/tracing/tracing/metrics/estimated_input_latency_metric.html |
@@ -0,0 +1,279 @@ |
+<!DOCTYPE html> |
+<!-- |
+Copyright (c) 2015 The Chromium Authors. All rights reserved. |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: this should be:
Copyright 2016 The Chromium
dproy
2016/09/20 01:11:42
Done.
|
+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/base/category_util.html"> |
+<link rel="import" href="/tracing/metrics/metric_registry.html"> |
+<link rel="import" href="/tracing/metrics/system_health/utils.html"> |
+<link rel="import" href="/tracing/model/helpers/chrome_model_helper.html"> |
+<link rel="import" href="/tracing/model/user_model/load_expectation.html"> |
+<link rel="import" href="/tracing/value/histogram.html"> |
+ |
+<script> |
+'use strict'; |
+ |
+tr.exportTo('tr.metrics', function() { |
+ |
+ var TOPLEVEL_CATEGORY_NAME = 'toplevel'; |
+ |
+ // TODO(dproy): Remove after we close #2784 |
+ function hasCategoryAndName(event, category, title) { |
+ return event.title === title && event.category && |
+ tr.b.getCategoryParts(event.category).indexOf(category) !== -1; |
charliea (OOO until 10-5)
2016/09/19 14:35:32
nit: line continuations are indented by 4 characte
dproy
2016/09/20 01:11:42
Done.
|
+ } |
+ |
+ function runConditionallyOnInnermostDescendants(slice, predicate, cb) { |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: what is an innermost descendant? Are these li
dproy
2016/09/20 01:11:43
This function is a little unfortunately named but
benjhayden
2016/09/20 05:52:35
Thanks for the explanation!
Would it be possible t
dproy
2016/09/20 15:21:06
One way to get around this would be to just look f
|
+ var succeededOnSomeDescendant = false; |
+ for (var child of slice.subSlices) { |
+ var succeededOnThisChild = runConditionallyOnInnermostDescendants( |
+ child, predicate, cb); |
charliea (OOO until 10-5)
2016/09/19 14:35:33
same (line continuations)
dproy
2016/09/20 01:11:43
Done.
|
+ succeededOnSomeDescendant = |
+ succeededOnThisChild || succeededOnSomeDescendant; |
charliea (OOO until 10-5)
2016/09/19 14:35:33
same (line continuations)
dproy
2016/09/20 01:11:43
Done.
|
+ } |
+ |
+ if (succeededOnSomeDescendant) { |
+ return true; |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: no need for braces around a single line branc
dproy
2016/09/20 01:11:43
Does our style guide forbid this? I really dislike
benjhayden
2016/09/20 05:52:35
For what it's worth, Ethan and I also wish that th
|
+ } |
+ |
+ if (!predicate(slice)) { |
+ return false; |
charliea (OOO until 10-5)
2016/09/19 14:35:33
same (single line branch)
|
+ } |
+ |
+ cb(slice); |
+ return true; |
+ } |
+ |
+ function forEachInnermostTopLevelSlices(thread, cb) { |
+ var topLevelPredicate = slice => tr.b.getCategoryParts(slice.category) |
charliea (OOO until 10-5)
2016/09/19 14:35:32
nit: I don't think we ever break at the ., but ins
dproy
2016/09/20 01:11:42
Done.
|
+ .includes(TOPLEVEL_CATEGORY_NAME); |
+ |
+ for (var slice of thread.sliceGroup.topLevelSlices) { |
charliea (OOO until 10-5)
2016/09/19 14:35:33
same (no need for braces)
|
+ runConditionallyOnInnermostDescendants(slice, topLevelPredicate, cb); |
+ } |
+ } |
+ |
+ function getAllInteractiveTimestampsSorted(model) { |
+ // TODO(dproy): When LoadExpectation v.1.0 is released, |
+ // update this function to use the new LoadExpectation rather |
+ // than calling loading_metric.html. |
+ |
+ var values = new tr.v.ValueSet(); |
+ tr.metrics.sh.loadingMetric(values, model); |
+ var ttiValues = values.getValuesNamed('timeToFirstInteractive'); |
charliea (OOO until 10-5)
2016/09/19 14:35:32
wrong indentation?
charliea (OOO until 10-5)
2016/09/19 14:35:32
wrong indentation?
dproy
2016/09/20 01:11:42
Done.
|
+ var interactiveTsList = []; |
+ for (var bin of tr.b.getOnlyElement(ttiValues).allBins) { |
+ for (var diagnostics of bin.diagnosticMaps) { |
+ var info = diagnostics.get('Navigation infos'); |
+ interactiveTsList.push(info.value.interactive); |
+ } |
+ } |
+ return interactiveTsList.sort((x, y) => x - y); |
+ } |
+ |
+ function getAllNavStartTimesSorted(rendererHelper) { |
+ var list = []; |
+ for (var ev of rendererHelper.mainThread.sliceGroup.childEvents()) { |
+ if (!hasCategoryAndName(ev, 'blink.user_timing', 'navigationStart')) |
+ continue; |
+ list.push(ev.start); |
+ } |
+ return list.sort((x, y) => x - y); |
+ } |
+ |
+ // A task window is defined as time from TTI until either |
+ // 1. beginning of next navigationStart event, or |
+ // 2. end of traces |
+ function getTaskWindows(interactiveTsList, navStartTimeList, endOfTraces) { |
+ var curNavStartTimeIndex = 0; |
+ var endOfLastWindow = -Infinity; |
+ var taskWindows = []; |
+ for (var curTTI of interactiveTsList) { |
+ var curNavStartTime = navStartTimeList[curNavStartTimeIndex]; |
+ while (curNavStartTime !== undefined && curNavStartTime < curTTI) { |
+ // There are possibly multiple navigationStart timestamps between |
+ // two interactive timestamps - the previous page load could |
+ // never reach interactive status. |
+ curNavStartTimeIndex++; |
+ curNavStartTime = navStartTimeList[curNavStartTimeIndex]; |
+ } |
+ |
+ if (curNavStartTime === endOfLastWindow) { |
+ // This is a violation of core assumption. |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: not need for braces
|
+ // TODO: When two pages share a render process, we can possibly |
+ // have two interactive time stamps between two navigation events. |
+ // If both interactive timestamps are reported, it is not clear how |
+ // to define estimated input latency. |
+ throw new Error("Two TTI timestamps with no navigation between them"); |
+ } |
+ |
+ if (curNavStartTime === undefined) { |
+ taskWindows.push({start: curTTI, end: endOfTraces}); |
+ endOfLastWindow = endOfTraces; |
+ continue; |
+ } |
+ |
+ taskWindows.push({start: curTTI, end: curNavStartTime}); |
+ endOfLastWindow = curNavStartTime; |
+ } |
+ return taskWindows; |
+ } |
+ |
+ /** |
+ * Note: Taken from |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: the first line of a jsdoc should be what the
dproy
2016/09/20 01:11:42
Done.
|
+ * https://github.com/GoogleChrome/lighthouse/blob/a5bbe2338fa474c94bb875849408704c81fec3df/lighthouse-core/lib/traces/tracing-processor.js#L121 |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: please create bit.ly or goo.gl shortlink and
dproy
2016/09/20 01:11:42
Done.
|
+ * |
+ * Calculate duration at specified percentiles for given population of |
charliea (OOO until 10-5)
2016/09/19 14:35:32
I'm not sure I understand what duration we're calc
dproy
2016/09/20 01:11:42
Clarified.
|
+ * durations. |
+ * If one of the durations overlaps the end of the window, the full |
charliea (OOO until 10-5)
2016/09/19 14:35:32
nit: if you want different paragraphs within a jsd
dproy
2016/09/20 01:11:42
Done.
|
+ * duration should be in the duration array, but the length not included |
+ * within the window should be given as `clippedLength`. For instance, if a |
+ * 50ms duration occurs 10ms before the end of the window, `50` should be in |
+ * the `durations` array, and `clippedLength` should be set to 40. |
+ * @see https://docs.google.com/document/d/18gvP-CBA2BiBpi3Rz1I1ISciKGhniTSZ9TY0XCnXS7E/preview |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: line is too long. Please create a shortlink t
dproy
2016/09/20 01:11:42
Done.
|
+ */ |
+ function calculateEILRiskPercentiles( |
+ durations, totalTime, percentiles, clippedLength) { |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: line continuations get four indents
dproy
2016/09/20 01:11:42
Done.
|
+ clippedLength = clippedLength || 0; |
+ |
+ var busyTime = tr.b.Statistics.sum(durations); |
+ busyTime -= clippedLength; |
+ |
+ // Start with idle time already compvare. |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: compvare => compare
benjhayden
2016/09/19 20:00:48
Actually, I think this is supposed to be "complete
dproy
2016/09/20 01:11:42
Yes this was indeed complete. s/let/var got me aga
|
+ var completedTime = totalTime - busyTime; |
+ var duration = 0; |
+ var cdfTime = completedTime; |
charliea (OOO until 10-5)
2016/09/19 14:35:33
I can kind of vaguely guess what cdfTime is, but I
dproy
2016/09/20 01:11:42
I didn't write this code, and I'm finding it very
|
+ var results = new Map(); |
+ |
+ var durationIndex = -1; |
+ var remainingCount = durations.length + 1; |
+ if (clippedLength > 0) { |
+ // If there was a clipped duration, one less in count |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: no need for braces
|
+ // since one hasn't started yet. |
+ remainingCount--; |
+ } |
+ |
+ // Find percentiles of interest, in order. |
+ for (var percentile of percentiles) { |
+ // Loop over durations, calculating a CDF value for each until it is above |
+ // the target percentile. |
+ var percentileTime = percentile * totalTime; |
+ while (cdfTime < percentileTime && durationIndex < durations.length - 1) { |
+ completedTime += duration; |
+ remainingCount += (duration >= 0 ? -1 : 1); |
+ |
+ if (clippedLength > 0 && clippedLength < durations[durationIndex + 1]) { |
+ duration = -clippedLength; |
+ clippedLength = 0; |
+ } else { |
+ durationIndex++; |
+ duration = durations[durationIndex]; |
+ } |
+ |
+ // Calculate value of CDF (multiplied by totalTime) for the end of |
+ // this duration. |
+ cdfTime = completedTime + Math.abs(duration) * remainingCount; |
+ } |
+ |
+ // Negative results are within idle time (0ms wait by definition), |
+ // so clamp at zero. |
+ results.set( |
+ percentile, |
charliea (OOO until 10-5)
2016/09/19 14:35:32
nit: line continuations get four indents
dproy
2016/09/20 01:11:42
Done.
|
+ Math.max(0, (percentileTime - completedTime) / remainingCount)); |
+ } |
+ |
+ return results; |
+ } |
+ |
+ /** |
+ * Note: This is adapted from |
+ * https://github.com/GoogleChrome/lighthouse/blob/a5bbe2338fa474c94bb875849408704c81fec3df/lighthouse-core/lib/traces/tracing-processor.js#L185 |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: shortlink.
dproy
2016/09/20 01:11:42
Done.
|
+ */ |
+ function getEQTPercentilesForWindow( |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: what the heck is an EQT?
dproy
2016/09/20 01:11:42
Added comments
|
+ percentiles, rendererHelper, startTime, endTime) { |
+ var totalTime = endTime - startTime; |
+ percentiles.sort((a, b) => a - b); |
+ |
+ var durations = []; |
+ var clippedLength = 0; |
+ forEachInnermostTopLevelSlices(rendererHelper.mainThread, slice => { |
+ // Discard slices outside range. |
+ if (slice.end <= startTime || slice.start >= endTime) { |
+ return; |
+ } |
+ |
+ // Clip any at edges of range. |
+ var duration = slice.duration; |
+ var sliceStart = slice.start; |
+ if (sliceStart < startTime) { |
+ // Any part of task before window can be discarded. |
+ sliceStart = startTime; |
+ duration = slice.end - sliceStart; |
+ |
+ } |
+ if (slice.end > endTime) { |
+ // Any part of task after window must be clipped but accounted for. |
+ clippedLength = duration - (endTime - sliceStart); |
+ } |
+ durations.push(duration); |
+ |
+ }); |
+ durations.sort((a, b) => a - b); |
+ return calculateEILRiskPercentiles( |
+ durations, totalTime, percentiles, clippedLength); |
+ } |
+ |
+ /** |
+ * @param {!tr.v.ValueSet} values |
+ * @param {!tr.model.Model} model |
+ * @param {!Object=} opt_options |
charliea (OOO until 10-5)
2016/09/19 14:35:32
what type of options are available? This might als
benjhayden
2016/09/19 20:00:48
This metric doesn't support any opt_options, so I
dproy
2016/09/20 01:11:42
Let me know if I should remove opt_options altoget
|
+ */ |
+ function estimatedInputLatencyMetric(values, model, opt_options) { |
+ |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: unnecessary blank line
dproy
2016/09/20 01:11:43
Done.
|
+ var chromeHelper = model.getOrCreateHelper( |
+ tr.model.helpers.ChromeModelHelper); |
+ // The largest pid renderer is probably the one we're interested in |
+ var rendererHelper = chromeHelper.rendererWithLargestPid; |
+ |
+ var interactiveTimestamps = getAllInteractiveTimestampsSorted(model); |
+ |
+ // We're assuming children iframes will never emit navigationStart events |
+ var navStartTimeList = getAllNavStartTimesSorted(rendererHelper); |
+ var taskWindowList = getTaskWindows( |
+ interactiveTimestamps, |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: line continuations
dproy
2016/09/20 01:11:43
Done.
|
+ navStartTimeList, |
+ rendererHelper.mainThread.bounds.max); |
+ |
+ // Compute the 90th percentile |
+ var eqtTargetPercentiles = [0.9]; |
+ |
+ var eqt90thPercentile = new tr.v.Histogram( |
+ 'Expected Queueing Time 90th Percentile', |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: line continuations
dproy
2016/09/20 01:11:42
Done.
|
+ tr.b.Unit.byName.timeDurationInMs_smallerIsBetter, |
+ tr.v.HistogramBinBoundaries.createExponential(0.1, 1e3, 100)); |
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: I think the best practice is to create these
dproy
2016/09/20 01:11:43
Done: made these top level constants with some com
|
+ |
+ for (var taskWindow of taskWindowList) { |
+ var eqtPercentiles = getEQTPercentilesForWindow( |
+ eqtTargetPercentiles, rendererHelper, taskWindow.start, taskWindow.end); |
charliea (OOO until 10-5)
2016/09/19 14:35:32
nit: line continuations
dproy
2016/09/20 01:11:43
Done.
|
+ var eqt90th = eqtPercentiles.get(0.9); |
+ if (eqt90th !== undefined) { |
+ eqt90thPercentile.addSample(eqt90th); |
charliea (OOO until 10-5)
2016/09/19 14:35:32
nit: single line branch
|
+ } |
+ } |
+ |
+ values.addHistogram(eqt90thPercentile); |
+ } |
+ |
+ tr.metrics.MetricRegistry.register(estimatedInputLatencyMetric, { |
+ supportsRangeOfInterest: false |
+ }); |
+ |
+ return { |
+ estimatedInputLatencyMetric: estimatedInputLatencyMetric, |
+ calculateEILRiskPercentiles: calculateEILRiskPercentiles, |
+ getEQTPercentilesForWindow: getEQTPercentilesForWindow |
+ }; |
+}); |
+</script> |