Skip to content
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

Merged
merged 15 commits into from
May 10, 2017
Merged

Conversation

patrickhulce
Copy link
Collaborator

@patrickhulce patrickhulce commented Apr 17, 2017

@patrickhulce
Copy link
Collaborator Author

PTAL :)

Copy link
Member

@paulirish paulirish left a 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) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forEach instead plz.

Copy link
Member

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

Copy link
Collaborator Author

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 => {
Copy link
Member

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
Copy link
Member

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?

Copy link
Collaborator Author

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
Copy link
Member

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?

Copy link
Collaborator Author

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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/inflight/inflightReqs/

Copy link
Collaborator Author

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 &&
Copy link
Member

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?

Copy link
Collaborator Author

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 &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and reuse here

Copy link
Member

@brendankenny brendankenny left a 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?

@brendankenny
Copy link
Member

brendankenny commented Apr 25, 2017

should this be in trace-of-tab?

er, that would be should this be in pwmetrics-events :)

@patrickhulce
Copy link
Collaborator Author

should this be in pwmetrics-events?

yep, added 👍

if this is what we're going to be pushing at I/O, should the various audits switching over to firstInteractive from TTI in #2013 be switching to this instead?

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...

@patrickhulce
Copy link
Collaborator Author

rebased and ready to take on the day 🌥 -> 🌤

@patrickhulce
Copy link
Collaborator Author

waiting for #2062 to add a real trace test

Copy link
Member

@brendankenny brendankenny left a 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Copy link
Collaborator Author

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',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Copy link
Collaborator Author

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

event.duration

Copy link
Collaborator Author

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)
Copy link
Member

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)

Copy link
Member

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

Copy link
Collaborator Author

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?

Copy link
Member

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) {
Copy link
Member

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)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Collaborator Author

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)

Copy link
Member

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

Copy link
Collaborator Author

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsdoc description

Copy link
Collaborator Author

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsdoc description :)

Copy link
Collaborator Author

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}
Copy link
Member

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 :)

Copy link
Collaborator Author

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') {
Copy link
Member

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?

Copy link
Collaborator Author

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});
Copy link
Member

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added typedef to findOverlappingQuietPeriods 👍

Copy link
Member

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

Copy link
Collaborator Author

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

@paulirish
Copy link
Member

I want this guy in ASAP, but not going to rush it. :)
@brendankenny your move here.

@patrickhulce
Copy link
Collaborator Author

FYI @brendankenny I split that part out into a separate PR so it's easier to review, PTAL #2197

@googlebot
Copy link

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 cla/google commit status will not change from this state. It's up to you to confirm consent of the commit author(s) and merge this pull request when appropriate.

@brendankenny
Copy link
Member

FYI @brendankenny I split that part out into a separate PR

🎉

@patrickhulce
Copy link
Collaborator Author

rebased and ready to go 💥

Copy link
Member

@brendankenny brendankenny left a 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});
Copy link
Member

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: !Array<!WebInspector.NetworkRequest>

Copy link
Collaborator Author

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!Array<!WebInspector.NetworkRequest>

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants