-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add consistently interactive audit #2023
Conversation
2d45f84
to
0a2fe77
Compare
PTAL :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only minor adjustments here. lgtm % those noted.
*/ | ||
static _findNetworkQuietPeriods(networkRecords, traceOfTab) { | ||
const traceEnd = traceOfTab.timestamps.traceEnd; | ||
const timeBoundaries = networkRecords.reduce((boundaries, record) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forEach instead plz.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a comment to clarify these timeBoundaries are just the timestamps of when requests start and end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
let inflight = 0; | ||
let quietPeriodStart = 0; | ||
const quietPeriods = []; | ||
timeBoundaries.forEach(boundary => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey a forEach! 😁 😍
const quietPeriods = []; | ||
timeBoundaries.forEach(boundary => { | ||
if (boundary.isStart) { | ||
// we are exiting a quiet period |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// we've just started a new request. are we exiting a quiet period?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
inflight++; | ||
} else { | ||
inflight--; | ||
// we are entering a quiet period |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// we've just completed a request. are we entering a new quiet period?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
return boundaries; | ||
}, []).sort((a, b) => a.time - b.time); | ||
|
||
let inflight = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/inflight/inflightReqs/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done-ish :)
const FMPTsInMs = traceOfTab.timestamps.firstMeaningfulPaint; | ||
|
||
const networkQuietPeriods = this._findNetworkQuietPeriods(networkRecords, traceOfTab) | ||
.filter(period => period.end > FMPTsInMs + REQUIRED_QUIET_WINDOW && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extract inner fn to const?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
.filter(period => period.end > FMPTsInMs + REQUIRED_QUIET_WINDOW && | ||
period.end - period.start >= REQUIRED_QUIET_WINDOW); | ||
const cpuQuietPeriods = this._findCPUQuietPeriods(longTasks, traceOfTab) | ||
.filter(period => period.end > FMPTsInMs + REQUIRED_QUIET_WINDOW && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and reuse here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
meta:
- should this be in trace-of-tab?
- if this is what we're going to be pushing at I/O, should the various audits switching over to firstInteractive from TTI in feat: add firstInteractive #2013 be switching to this instead?
er, that would be should this be in |
yep, added 👍
sadly, yes. I replaced it with firstInteractive since it's most comparable to our previous definition in spirit compared to the pessimism of this one, but we'll probably want to switch the load fast enough for PWA one sometime before I/O if that policy sticks, it has unfortunate effects for other audits like the unused images since by definition consistently interactive would wait for the images to finish, so I'm not sold on switching everything just yet... |
rebased and ready to take on the day 🌥 -> 🌤 |
waiting for #2062 to add a real trace test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
review!
// https://www.desmos.com/calculator/uti67afozh | ||
const SCORING_POINT_OF_DIMINISHING_RETURNS = 1700; | ||
const SCORING_MEDIAN = 10000; | ||
const SCORING_TARGET = 5000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
description: 'Consistently Interactive (beta)', | ||
helpText: 'The point at which most network resources have finished loading and the ' + | ||
'CPU is idle for a prolonged period.', | ||
optimalValue: SCORING_TARGET.toLocaleString() + 'ms', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
const longTasks = TracingProcessor.getMainThreadTopLevelEvents(traceModel, trace) | ||
.filter(event => event.end - event.start >= 50); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
event.duration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
throw new Error('No firstMeaningfulPaint found in trace.'); | ||
} | ||
|
||
const longTasks = TracingProcessor.getMainThreadTopLevelEvents(traceModel, trace) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pass in FMP
here to avoid computing stuff before then? (also would allow removal of FMP stuff in isLongEnoughQuietPeriod
above)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
though I guess _findNetworkQuietPeriods
would then also need to find out and clip by FMP
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't really be able to remove anything since we still need to check that the period is 5s after FMP (even if long tasks are ignored before FMP) so it's just the saved long tasks we're iterating through, and IMO it makes the logic harder to parse that FMP has already been dealt with in CPU but we still have to deal with it in network, fine leaving?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fine leaving?
yeah, I guess it's just awkward one way or the other. SG
|
||
return Promise.all(computedTraceArtifacts) | ||
.then(([traceModel, traceOfTab]) => { | ||
if (!traceOfTab.timestamps.firstMeaningfulPaint) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should probably throw on lack of DCL
too (as in TTFI)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(though in the spec I'm not actually sure this is true, it just seemed like the proper thing to do. I should raise it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(though in the spec I'm not actually sure this is true, it just seemed like the proper thing to do. I should raise it)
wouldn't no FMP be a bug in Chrome/Lighthouse? Unless you mean something else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was talking about the fact that we're doing the max of DCL and the value. I wasn't sure that's in the spec or if I added that to be sure it doesn't happen before TTFI
} | ||
|
||
/** | ||
* @param {!Array<{start: number, end: number}>} longTasks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jsdoc description
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
/** | ||
* @param {!Array<{start: number, end: number}>} longTasks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jsdoc description :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
* @param {!Array<{start: number, end: number}>} longTasks | ||
* @param {{timestamps: {navigationStart: number, firstMeaningfulPaint: number, | ||
* traceEnd: number}}} traceOfTab | ||
* @return {!Object} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be great to have a type here :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
const timeBoundaries = []; | ||
networkRecords.forEach(record => { | ||
const scheme = record.parsedURL && record.parsedURL.scheme; | ||
if (scheme === 'data' || scheme === 'ws') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be overkill, but should we convert this to a ignoredNetworkSchemes
array and pull it into a constant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, done
traceOfTab.timestamps.domContentLoaded | ||
) * 1000; | ||
const timeInMs = (timestamp - traceOfTab.timestamps.navigationStart * 1000) / 1000; | ||
const extendedInfo = Object.assign(quietPeriodInfo, {timestamp, timeInMs}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shape of quietPeriodInfo
is difficult to track down. Can it be made more evident here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added typedef to findOverlappingQuietPeriods
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that is...not a terribly useful type definition :P If I wanted to use that extendedInfo
I would have to check the param
signatures of _findCPUQuietPeriods
and _findNetworkQuietPeriods
and read findOverlappingQuietPeriods
to make sure there's only one code escape path. We don't have a great place for documenting artifact shapes, but it would be nice to always do it, even if in an awkward place
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a TimePeriod
typedef
/**
* @typedef {{
* start: number,
* end: number,
* }}
*/
let TimePeriod; // eslint-disable-line no-unused-vars
I want this guy in ASAP, but not going to rush it. :) |
FYI @brendankenny I split that part out into a separate PR so it's easier to review, PTAL #2197 |
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that they're okay with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
🎉 |
rebased and ready to go 💥 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
last documentation nits. PR looks great.
traceOfTab.timestamps.domContentLoaded | ||
) * 1000; | ||
const timeInMs = (timestamp - traceOfTab.timestamps.navigationStart * 1000) / 1000; | ||
const extendedInfo = Object.assign(quietPeriodInfo, {timestamp, timeInMs}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that is...not a terribly useful type definition :P If I wanted to use that extendedInfo
I would have to check the param
signatures of _findCPUQuietPeriods
and _findNetworkQuietPeriods
and read findOverlappingQuietPeriods
to make sure there's only one code escape path. We don't have a great place for documenting artifact shapes, but it would be nice to always do it, even if in an awkward place
/** | ||
* Finds all time periods where the number of inflight requests is less than or equal to the | ||
* number of allowed concurrent requests (2). | ||
* @param {!Array<WebInspector.NetworkRequest>} networkRecords |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: !Array<!WebInspector.NetworkRequest>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
/** | ||
* Finds the first time period where a network quiet period and a CPU quiet period overlap. | ||
* @param {!Array<{start: number, end: number}>} longTasks | ||
* @param {!Array<WebInspector.NetworkRequest>} networkRecords |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!Array<!WebInspector.NetworkRequest>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
https://docs.google.com/document/d/1GGiI9-7KeY3TPqS3YT271upUVimo-XiL5mwWorDUD4c/preview