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 |