Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 <!DOCTYPE html> | |
| 2 <!-- | |
| 3 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.
| |
| 4 Use of this source code is governed by a BSD-style license that can be | |
| 5 found in the LICENSE file. | |
| 6 --> | |
| 7 | |
| 8 <link rel="import" href="/tracing/base/category_util.html"> | |
| 9 <link rel="import" href="/tracing/metrics/metric_registry.html"> | |
| 10 <link rel="import" href="/tracing/metrics/system_health/utils.html"> | |
| 11 <link rel="import" href="/tracing/model/helpers/chrome_model_helper.html"> | |
| 12 <link rel="import" href="/tracing/model/user_model/load_expectation.html"> | |
| 13 <link rel="import" href="/tracing/value/histogram.html"> | |
| 14 | |
| 15 <script> | |
| 16 'use strict'; | |
| 17 | |
| 18 tr.exportTo('tr.metrics', function() { | |
| 19 | |
| 20 var TOPLEVEL_CATEGORY_NAME = 'toplevel'; | |
| 21 | |
| 22 // TODO(dproy): Remove after we close #2784 | |
| 23 function hasCategoryAndName(event, category, title) { | |
| 24 return event.title === title && event.category && | |
| 25 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.
| |
| 26 } | |
| 27 | |
| 28 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
| |
| 29 var succeededOnSomeDescendant = false; | |
| 30 for (var child of slice.subSlices) { | |
| 31 var succeededOnThisChild = runConditionallyOnInnermostDescendants( | |
| 32 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.
| |
| 33 succeededOnSomeDescendant = | |
| 34 succeededOnThisChild || succeededOnSomeDescendant; | |
|
charliea (OOO until 10-5)
2016/09/19 14:35:33
same (line continuations)
dproy
2016/09/20 01:11:43
Done.
| |
| 35 } | |
| 36 | |
| 37 if (succeededOnSomeDescendant) { | |
| 38 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
| |
| 39 } | |
| 40 | |
| 41 if (!predicate(slice)) { | |
| 42 return false; | |
|
charliea (OOO until 10-5)
2016/09/19 14:35:33
same (single line branch)
| |
| 43 } | |
| 44 | |
| 45 cb(slice); | |
| 46 return true; | |
| 47 } | |
| 48 | |
| 49 function forEachInnermostTopLevelSlices(thread, cb) { | |
| 50 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.
| |
| 51 .includes(TOPLEVEL_CATEGORY_NAME); | |
| 52 | |
| 53 for (var slice of thread.sliceGroup.topLevelSlices) { | |
|
charliea (OOO until 10-5)
2016/09/19 14:35:33
same (no need for braces)
| |
| 54 runConditionallyOnInnermostDescendants(slice, topLevelPredicate, cb); | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 function getAllInteractiveTimestampsSorted(model) { | |
| 59 // TODO(dproy): When LoadExpectation v.1.0 is released, | |
| 60 // update this function to use the new LoadExpectation rather | |
| 61 // than calling loading_metric.html. | |
| 62 | |
| 63 var values = new tr.v.ValueSet(); | |
| 64 tr.metrics.sh.loadingMetric(values, model); | |
| 65 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.
| |
| 66 var interactiveTsList = []; | |
| 67 for (var bin of tr.b.getOnlyElement(ttiValues).allBins) { | |
| 68 for (var diagnostics of bin.diagnosticMaps) { | |
| 69 var info = diagnostics.get('Navigation infos'); | |
| 70 interactiveTsList.push(info.value.interactive); | |
| 71 } | |
| 72 } | |
| 73 return interactiveTsList.sort((x, y) => x - y); | |
| 74 } | |
| 75 | |
| 76 function getAllNavStartTimesSorted(rendererHelper) { | |
| 77 var list = []; | |
| 78 for (var ev of rendererHelper.mainThread.sliceGroup.childEvents()) { | |
| 79 if (!hasCategoryAndName(ev, 'blink.user_timing', 'navigationStart')) | |
| 80 continue; | |
| 81 list.push(ev.start); | |
| 82 } | |
| 83 return list.sort((x, y) => x - y); | |
| 84 } | |
| 85 | |
| 86 // A task window is defined as time from TTI until either | |
| 87 // 1. beginning of next navigationStart event, or | |
| 88 // 2. end of traces | |
| 89 function getTaskWindows(interactiveTsList, navStartTimeList, endOfTraces) { | |
| 90 var curNavStartTimeIndex = 0; | |
| 91 var endOfLastWindow = -Infinity; | |
| 92 var taskWindows = []; | |
| 93 for (var curTTI of interactiveTsList) { | |
| 94 var curNavStartTime = navStartTimeList[curNavStartTimeIndex]; | |
| 95 while (curNavStartTime !== undefined && curNavStartTime < curTTI) { | |
| 96 // There are possibly multiple navigationStart timestamps between | |
| 97 // two interactive timestamps - the previous page load could | |
| 98 // never reach interactive status. | |
| 99 curNavStartTimeIndex++; | |
| 100 curNavStartTime = navStartTimeList[curNavStartTimeIndex]; | |
| 101 } | |
| 102 | |
| 103 if (curNavStartTime === endOfLastWindow) { | |
| 104 // This is a violation of core assumption. | |
|
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: not need for braces
| |
| 105 // TODO: When two pages share a render process, we can possibly | |
| 106 // have two interactive time stamps between two navigation events. | |
| 107 // If both interactive timestamps are reported, it is not clear how | |
| 108 // to define estimated input latency. | |
| 109 throw new Error("Two TTI timestamps with no navigation between them"); | |
| 110 } | |
| 111 | |
| 112 if (curNavStartTime === undefined) { | |
| 113 taskWindows.push({start: curTTI, end: endOfTraces}); | |
| 114 endOfLastWindow = endOfTraces; | |
| 115 continue; | |
| 116 } | |
| 117 | |
| 118 taskWindows.push({start: curTTI, end: curNavStartTime}); | |
| 119 endOfLastWindow = curNavStartTime; | |
| 120 } | |
| 121 return taskWindows; | |
| 122 } | |
| 123 | |
| 124 /** | |
| 125 * 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.
| |
| 126 * https://github.com/GoogleChrome/lighthouse/blob/a5bbe2338fa474c94bb87584940 8704c81fec3df/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.
| |
| 127 * | |
| 128 * 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.
| |
| 129 * durations. | |
| 130 * 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.
| |
| 131 * duration should be in the duration array, but the length not included | |
| 132 * within the window should be given as `clippedLength`. For instance, if a | |
| 133 * 50ms duration occurs 10ms before the end of the window, `50` should be in | |
| 134 * the `durations` array, and `clippedLength` should be set to 40. | |
| 135 * @see https://docs.google.com/document/d/18gvP-CBA2BiBpi3Rz1I1ISciKGhniTSZ9T Y0XCnXS7E/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.
| |
| 136 */ | |
| 137 function calculateEILRiskPercentiles( | |
| 138 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.
| |
| 139 clippedLength = clippedLength || 0; | |
| 140 | |
| 141 var busyTime = tr.b.Statistics.sum(durations); | |
| 142 busyTime -= clippedLength; | |
| 143 | |
| 144 // 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
| |
| 145 var completedTime = totalTime - busyTime; | |
| 146 var duration = 0; | |
| 147 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
| |
| 148 var results = new Map(); | |
| 149 | |
| 150 var durationIndex = -1; | |
| 151 var remainingCount = durations.length + 1; | |
| 152 if (clippedLength > 0) { | |
| 153 // 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
| |
| 154 // since one hasn't started yet. | |
| 155 remainingCount--; | |
| 156 } | |
| 157 | |
| 158 // Find percentiles of interest, in order. | |
| 159 for (var percentile of percentiles) { | |
| 160 // Loop over durations, calculating a CDF value for each until it is above | |
| 161 // the target percentile. | |
| 162 var percentileTime = percentile * totalTime; | |
| 163 while (cdfTime < percentileTime && durationIndex < durations.length - 1) { | |
| 164 completedTime += duration; | |
| 165 remainingCount += (duration >= 0 ? -1 : 1); | |
| 166 | |
| 167 if (clippedLength > 0 && clippedLength < durations[durationIndex + 1]) { | |
| 168 duration = -clippedLength; | |
| 169 clippedLength = 0; | |
| 170 } else { | |
| 171 durationIndex++; | |
| 172 duration = durations[durationIndex]; | |
| 173 } | |
| 174 | |
| 175 // Calculate value of CDF (multiplied by totalTime) for the end of | |
| 176 // this duration. | |
| 177 cdfTime = completedTime + Math.abs(duration) * remainingCount; | |
| 178 } | |
| 179 | |
| 180 // Negative results are within idle time (0ms wait by definition), | |
| 181 // so clamp at zero. | |
| 182 results.set( | |
| 183 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.
| |
| 184 Math.max(0, (percentileTime - completedTime) / remainingCount)); | |
| 185 } | |
| 186 | |
| 187 return results; | |
| 188 } | |
| 189 | |
| 190 /** | |
| 191 * Note: This is adapted from | |
| 192 * https://github.com/GoogleChrome/lighthouse/blob/a5bbe2338fa474c94bb87584940 8704c81fec3df/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.
| |
| 193 */ | |
| 194 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
| |
| 195 percentiles, rendererHelper, startTime, endTime) { | |
| 196 var totalTime = endTime - startTime; | |
| 197 percentiles.sort((a, b) => a - b); | |
| 198 | |
| 199 var durations = []; | |
| 200 var clippedLength = 0; | |
| 201 forEachInnermostTopLevelSlices(rendererHelper.mainThread, slice => { | |
| 202 // Discard slices outside range. | |
| 203 if (slice.end <= startTime || slice.start >= endTime) { | |
| 204 return; | |
| 205 } | |
| 206 | |
| 207 // Clip any at edges of range. | |
| 208 var duration = slice.duration; | |
| 209 var sliceStart = slice.start; | |
| 210 if (sliceStart < startTime) { | |
| 211 // Any part of task before window can be discarded. | |
| 212 sliceStart = startTime; | |
| 213 duration = slice.end - sliceStart; | |
| 214 | |
| 215 } | |
| 216 if (slice.end > endTime) { | |
| 217 // Any part of task after window must be clipped but accounted for. | |
| 218 clippedLength = duration - (endTime - sliceStart); | |
| 219 } | |
| 220 durations.push(duration); | |
| 221 | |
| 222 }); | |
| 223 durations.sort((a, b) => a - b); | |
| 224 return calculateEILRiskPercentiles( | |
| 225 durations, totalTime, percentiles, clippedLength); | |
| 226 } | |
| 227 | |
| 228 /** | |
| 229 * @param {!tr.v.ValueSet} values | |
| 230 * @param {!tr.model.Model} model | |
| 231 * @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
| |
| 232 */ | |
| 233 function estimatedInputLatencyMetric(values, model, opt_options) { | |
| 234 | |
|
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: unnecessary blank line
dproy
2016/09/20 01:11:43
Done.
| |
| 235 var chromeHelper = model.getOrCreateHelper( | |
| 236 tr.model.helpers.ChromeModelHelper); | |
| 237 // The largest pid renderer is probably the one we're interested in | |
| 238 var rendererHelper = chromeHelper.rendererWithLargestPid; | |
| 239 | |
| 240 var interactiveTimestamps = getAllInteractiveTimestampsSorted(model); | |
| 241 | |
| 242 // We're assuming children iframes will never emit navigationStart events | |
| 243 var navStartTimeList = getAllNavStartTimesSorted(rendererHelper); | |
| 244 var taskWindowList = getTaskWindows( | |
| 245 interactiveTimestamps, | |
|
charliea (OOO until 10-5)
2016/09/19 14:35:33
nit: line continuations
dproy
2016/09/20 01:11:43
Done.
| |
| 246 navStartTimeList, | |
| 247 rendererHelper.mainThread.bounds.max); | |
| 248 | |
| 249 // Compute the 90th percentile | |
| 250 var eqtTargetPercentiles = [0.9]; | |
| 251 | |
| 252 var eqt90thPercentile = new tr.v.Histogram( | |
| 253 '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.
| |
| 254 tr.b.Unit.byName.timeDurationInMs_smallerIsBetter, | |
| 255 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
| |
| 256 | |
| 257 for (var taskWindow of taskWindowList) { | |
| 258 var eqtPercentiles = getEQTPercentilesForWindow( | |
| 259 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.
| |
| 260 var eqt90th = eqtPercentiles.get(0.9); | |
| 261 if (eqt90th !== undefined) { | |
| 262 eqt90thPercentile.addSample(eqt90th); | |
|
charliea (OOO until 10-5)
2016/09/19 14:35:32
nit: single line branch
| |
| 263 } | |
| 264 } | |
| 265 | |
| 266 values.addHistogram(eqt90thPercentile); | |
| 267 } | |
| 268 | |
| 269 tr.metrics.MetricRegistry.register(estimatedInputLatencyMetric, { | |
| 270 supportsRangeOfInterest: false | |
| 271 }); | |
| 272 | |
| 273 return { | |
| 274 estimatedInputLatencyMetric: estimatedInputLatencyMetric, | |
| 275 calculateEILRiskPercentiles: calculateEILRiskPercentiles, | |
| 276 getEQTPercentilesForWindow: getEQTPercentilesForWindow | |
| 277 }; | |
| 278 }); | |
| 279 </script> | |
| OLD | NEW |