Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(166)

Unified Diff: tracing/tracing/metrics/estimated_input_latency_metric.html

Issue 2323533003: [Not for landing - CL being split] Add Estimated Input Latency - EQT 90th Percentile definition
Patch Set: Delete useless empty loop Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tracing/tracing/metrics/all_metrics.html ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..c6fe5c9c114b25e093ac127f3f981ea25cad6468
--- /dev/null
+++ b/tracing/tracing/metrics/estimated_input_latency_metric.html
@@ -0,0 +1,303 @@
+<!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/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">
+<link rel="import" href="/tracing/value/value.html">
+
+<script>
+'use strict';
+
+tr.exportTo('tr.metrics', function() {
+
+ var TOPLEVEL_CATEGORY_NAME = 'toplevel';
+
+ // TODO(dproy): Figure out if this is really necessary
+ var BASE_RESPONSE_LATENCY = 0;
+
+ // TODO(dproy): This is copy pasta from loading_metric.html. Factor out.
benjhayden 2016/09/08 16:03:26 https://github.com/catapult-project/catapult/issue
dproy 2016/09/08 19:45:53 Ok I'm changing the TODO here to wait until that i
+ function hasCategoryAndName(event, category, title) {
+ return event.title === title && event.category &&
+ tr.b.getCategoryParts(event.category).indexOf(category) !== -1;
+ }
+
+ // TODO (dproy): This is copy pasta from loading_metric.html. Factor out.
benjhayden 2016/09/08 16:03:26 Feel free to move it to ChromeBrowserHelper.
dproy 2016/09/08 19:45:53 Done. I moved it to ChromeModelHelper because it f
+ function findTargetRendererHelper(chromeHelper) {
+ var largestPid = -1;
+ for (var pid in chromeHelper.rendererHelpers) {
+ var rendererHelper = chromeHelper.rendererHelpers[pid];
+ if (rendererHelper.isChromeTracingUI)
+ continue;
+ if (pid > largestPid)
+ largestPid = pid;
+ }
+
+ if (largestPid === -1)
+ return undefined;
+
+ return chromeHelper.rendererHelpers[largestPid];
+ }
+
+ function runConditionallyOnInnermostDescendants(slice, predicate, cb) {
+ var succeededOnSomeDescendant = false;
+ for (var child of slice.subSlices) {
+ var succeededOnThisChild = runConditionallyOnInnermostDescendants(
+ child, predicate, cb);
+ succeededOnSomeDescendant =
+ succeededOnThisChild || succeededOnSomeDescendant;
+ }
+
+ if (succeededOnSomeDescendant) return true;
benjhayden 2016/09/08 16:03:26 Why not move this early return up into the loop so
dproy 2016/09/08 19:45:53 I actually want it to run on all siblings. The pri
+
+ if (predicate(slice)) {
+ cb(slice);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function isTopLevelSlice(slice) {
benjhayden 2016/09/08 16:03:26 This name is confusing. Slice and AsyncSlice have
dproy 2016/09/08 19:45:53 Done.
+ return tr.b.getCategoryParts(slice.category)
+ .includes(TOPLEVEL_CATEGORY_NAME);
+ }
+
+ function forEachInnermostTopLevelSlices(thread, cb) {
+ for (var slice of thread.sliceGroup.topLevelSlices) {
benjhayden 2016/09/08 16:03:26 I seem to recall the existence of tracing categori
dproy 2016/09/08 19:45:53 That should be ok. I actually want the biggest and
+ runConditionallyOnInnermostDescendants(slice, isTopLevelSlice, cb);
+ }
+ }
+
+ function getAllInteractiveTimestampsSorted(model) {
+ // TODO(dproy): When LoadExpectation v.1.0 is released,
benjhayden 2016/09/08 16:03:26 I'll take care of this refactor. You can either ch
dproy 2016/09/08 19:45:53 Ok I'll remove the TODO for me here. Thanks!
+ // 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');
+ var interactiveTsList = [];
+ for (var bin of tr.b.getOnlyElement(ttiValues).numeric.allBins)
+ for (var diagnostics of bin.diagnosticMaps) {
+ var breakdown = diagnostics.get('breakdown');
+ interactiveTsList.push(breakdown.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.
+ // 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
+ * https://github.com/GoogleChrome/lighthouse/blob/a5bbe2338fa474c94bb875849408704c81fec3df/lighthouse-core/lib/traces/tracing-processor.js#L121
+ *
+ * Calculate duration at specified percentiles for given population of
+ * durations.
+ * If one of the durations overlaps the end of the window, the full
+ * 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
+ */
+ function calculateRiskPercentiles(
+ durations, totalTime, percentiles, clippedLength) {
+ clippedLength = clippedLength || 0;
+
+ var busyTime = 0;
+ for (var i = 0; i < durations.length; i++) {
+ busyTime += durations[i];
+
+ }
+ busyTime -= clippedLength;
+
+ // Start with idle time already compvare.
+ var compvaredTime = totalTime - busyTime;
+ var duration = 0;
+ var cdfTime = compvaredTime;
+ var results = [];
+
+ var durationIndex = -1;
+ var remainingCount = durations.length + 1;
+ if (clippedLength > 0) {
+ // If there was a clipped duration, one less in count
+ // 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 percentivarime = percentile * totalTime;
+ while (cdfTime < percentivarime && durationIndex < durations.length - 1) {
+ compvaredTime += 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 = compvaredTime + Math.abs(duration) * remainingCount;
+ }
+
+ // Negative results are within idle time (0ms wait by definition),
+ // so clamp at zero.
+ results.push({
+ percentile,
+ time: Math.max(0, (percentivarime - compvaredTime) / remainingCount) +
+ BASE_RESPONSE_LATENCY
+ });
+ }
+
+ return results;
+ }
+
+ /**
+ * Note: This is adapted from
+ * https://github.com/GoogleChrome/lighthouse/blob/a5bbe2338fa474c94bb875849408704c81fec3df/lighthouse-core/lib/traces/tracing-processor.js#L185
+ */
+ function getEQTPercentilesForWindow(
+ 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.
+ let duration = slice.duration;
+ let 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 calculateRiskPercentiles(
+ durations, totalTime, percentiles, clippedLength);
+ }
+
+ /**
+ * @param {!tr.v.ValueSet} values
+ * @param {!tr.model.Model} model
+ * @param {!Object=} opt_options
+ */
+ function estimatedInputLatencyMetric(values, model, opt_options) {
+
+ var chromeHelper = model.getOrCreateHelper(
+ tr.model.helpers.ChromeModelHelper);
+ var rendererHelper = findTargetRendererHelper(chromeHelper);
+
+ var interactiveTimestamps = getAllInteractiveTimestampsSorted(model);
+
+ // We're assuming children iframes will never emit navigationStart events
+ var navStartTimeList = getAllNavStartTimesSorted(rendererHelper);
+ var taskWindowList = getTaskWindows(
+ interactiveTimestamps,
+ navStartTimeList,
+ rendererHelper.mainThread.bounds.max);
+
+ var eqtTargetPercentiles = [.9];
+
+ var EQT90thPercentile = new tr.v.Histogram(
+ tr.v.Unit.byName.timeDurationInMs_smallerIsBetter,
+ tr.v.HistogramBinBoundaries.createExponential(0.1, 1e3, 100));
+
+ for (var taskWindow of taskWindowList) {
+ var eqtPercentiles = getEQTPercentilesForWindow(
+ eqtTargetPercentiles, rendererHelper, taskWindow.start, taskWindow.end);
+ var filtered = eqtPercentiles.filter(res => res.percentile === 0.9)
+ var EQT90th = eqtPercentiles.filter(res => res.percentile === 0.9)[0];
+ if (EQT90th !== undefined) EQT90thPercentile.addSample(EQT90th.time);
+ }
+
+ values.addValue(new tr.v.NumericValue(
+ 'Expected Queueing Time 90th Percentile', EQT90thPercentile,
+ { description: '90th percetile of expected queueing time for ' +
+ 'uniformly distributed random input event after TTI' }));
+ }
+
+ tr.metrics.MetricRegistry.register(estimatedInputLatencyMetric, {
+ supportsRangeOfInterest: false
+ });
+
+ return {
+ estimatedInputLatencyMetric: estimatedInputLatencyMetric,
+ };
+});
+</script>
« no previous file with comments | « tracing/tracing/metrics/all_metrics.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698