-
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
core: merge render blocking audits to lantern #4995
Conversation
I think we should keep them separate. :) Handling RB stylesheets is done via reducing, inlining and the critical CSS dance.Handling RB scripts involved a bunch of different techniques. I think they're quite distinct in how you have to reason about them, even though at a browser level they are similar. So i feel its still valuable to report on them individually. Also getting to avoid the super generic "resources" is great.. |
Discussed this more. Here are the options that we could do:
|
@paulirish says
on it. |
gentle reminder this is awaitin' review once again :) |
}); | ||
|
||
graphWithoutChildren.record._transferSize += totalChildNetworkBytes; | ||
const estimateAfterInlineA = simulator.simulate(graphWithoutChildren); |
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.
A
?
And why not just pull the property off this in one-line?
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.
estimateAfterInlining
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.
just leftover from debugging, fixed
* @param {Map<string, number>} wastedBytesMap | ||
* @return {number} | ||
*/ | ||
static estimateSavingsFromInlining(simulator, fcpGraph, deferredIds, wastedBytesMap) { |
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 some comments? it's taking me a long time to grok what's happening within 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.
Estimate savings from deferring render-blocking requests and inlining used CSS into the HTML response.
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.
Also a lot of what you wrote in the awesome PR description can be moved into comments. 😀
like
the new approach is computing "how much faster would this page reach FCP if we inlined all the used CSS from render blocking stylesheets and deferred all scripts" as a result the savings we report are slightly more conservative but much more accurate.
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
// i.e. the referenced font asset won't become inlined just because you inline the CSS | ||
node.traverse(node => deferredNodeIds.add(node.id)); | ||
|
||
const wastedMs = nodeTiming.endTime - nodeTiming.startTime; |
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.
The wasted cost of each request is from requestSent to responseReceived
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.
btw do you want to round this?
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 willDefer = deferredIds.has(node.id); | ||
if (willDefer && node.type === Node.TYPES.NETWORK && | ||
node.record._resourceType === WebInspector.resourceTypes.Stylesheet) { | ||
const wastedBytes = wastedBytesMap.get(node.record.url) || 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.
Assume we defer the network request till later, and mark it non-blocking
right?
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.
ya, comment added
return !willDefer; | ||
}); | ||
|
||
graphWithoutChildren.record._transferSize += totalChildNetworkBytes; |
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.
Add inlined bytes back to the HTML response
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 {Map<string, number>} wastedBytesMap | ||
* @return {number} | ||
*/ | ||
static estimateSavingsFromInlining(simulator, fcpGraph, deferredIds, wastedBytesMap) { |
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.
also since the inlining only applies to CSS but this affects both JS and CSS it should be named something that's more balanced. estimateSavingsFromDeferring
sg. wdyt?
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.
since it's doing both, I just renamed to estimateSavingsWithGraphs
and added better description 👍
const originalEstimate = simulator.simulate(fcpGraph).timeInMs; | ||
|
||
let totalChildNetworkBytes = 0; | ||
const graphWithoutChildren = fcpGraph.cloneWithRelationships(node => { |
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.
why is it called this btw? can't really make sense of 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.
it was leftover from when everything was inlined, changed to minimalFCPGraph
* @param {Map<string, number>} wastedBytesMap | ||
* @return {number} | ||
*/ | ||
static estimateSavingsFromInlining(simulator, fcpGraph, deferredIds, wastedBytesMap) { |
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 can you also add a comment that we don't inline JS because we assume they can get a [defer] attribute
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 fcpTsInMs = traceOfTab.timestamps.firstContentfulPaint / 1000; | ||
|
||
const nodeTimingMap = fcpSimulation.optimisticEstimate.nodeTiming; | ||
const nodesByUrl = keyByUrl(Array.from(nodeTimingMap.keys())); |
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 we instead just have a helper function of like getNodeTimingForUrl(nodeTimingMap, url){ ..
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 the helper function loops through the keys to find the matching record?
const fcpSimulation = await artifacts.requestFirstContentfulPaint(metricComputationData); | ||
const fcpTsInMs = traceOfTab.timestamps.firstContentfulPaint / 1000; | ||
|
||
const nodeTimingMap = fcpSimulation.optimisticEstimate.nodeTiming; |
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.
side note i wish this was called nodeTimings
or nodeTimingMap
to differentiate from the singular case within 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.
yeah we can rename in separate PR 👍
@paulirish all done 'cept the helper method business, not 100% I understood what that wins us |
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.
apparently too slow
|
||
const {node, nodeTiming} = nodesByUrl[resource.tag.url]; | ||
|
||
// Mark this node and all it's dependents as deferrable |
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.
its
static async computeWastedCSSBytes(artifacts, context) { | ||
const wastedBytesByUrl = new Map(); | ||
try { | ||
const results = await UnusedCSS.audit(artifacts, context); |
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.
maybe a TODO that this should be pulled out into a computed artifact?
for (const item of results.details.items) { | ||
wastedBytesByUrl.set(item.url, item.wastedBytes); | ||
} | ||
} catch (_) {} |
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.
whats the failure mode 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.
assume all bytes were used and inline all CSS
const simulatorData = {devtoolsLog, settings: context.settings}; | ||
const traceOfTab = await artifacts.requestTraceOfTab(trace); | ||
const simulator = await artifacts.requestLoadSimulator(simulatorData); | ||
const wastedBytesMap = await RenderBlockingResources.computeWastedCSSBytes(artifacts, context); |
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.
wastedCssBytesMap
? Or it's more like cssBytesByUrl
or something in function
So this turned out to be quite a doozy 😩
Changes in this PR:
What's different about the savings calculation?
ref #4333