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

new_audit(bootup-time): Bootup time per script #3563

Merged
merged 7 commits into from
Nov 17, 2017
Merged

Conversation

wardpeet
Copy link
Collaborator

@wardpeet wardpeet commented Oct 14, 2017

show bootup time per script

Fixes part of #3105

@wardpeet
Copy link
Collaborator Author

Devtools is not giving me parse time of scripts :(
I can look in the trace for v8.parseOnBackground?

Also when scripts are not moved to v8.parseOnBackground than I can't seem to find any parse event.

any idea @paulirish ?

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.

generally the correct model, but some small suggestions. holler on IM if you have questions

const timelineModel = new DevtoolsTimelineModel(trace);
const bottomUpByName = timelineModel.bottomUpGroupBy('URL');
const result = new Map();
bottomUpByName.children.forEach((value, url) => {
Copy link
Member

Choose a reason for hiding this comment

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

a bit of bikeshedding?

value ==> perUrlNode ?

return;
}

const evaluateTime = value.children.get('EvaluateScript:@' + url) || {};
Copy link
Member

Choose a reason for hiding this comment

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

we could do this but i'd rather iterate over the perUrlNode children (which would be perTaskPerUrlNode i guess)...

so iterate over them.. then grab perTaskPerUrlNode.event.name. This should give you v8.compile and EvaluateScript. We can then reuse this stuff:

const group = {
loading: 'Network request loading',
parseHTML: 'Parsing DOM',
styleLayout: 'Style & Layout',
compositing: 'Compositing',
painting: 'Paint',
gpu: 'GPU',
scripting: 'Script Evaluation',
scriptParseCompile: 'Script Parsing & Compile',
scriptGC: 'Garbage collection',
other: 'Other',
images: 'Images',
};
const taskToGroup = {
'Animation': group.painting,
'Async Task': group.other,
'Frame Start': group.painting,
'Frame Start (main thread)': group.painting,
'Cancel Animation Frame': group.scripting,
'Cancel Idle Callback': group.scripting,
'Compile Script': group.scriptParseCompile,
'Composite Layers': group.compositing,
'Console Time': group.scripting,
'Image Decode': group.images,
'Draw Frame': group.painting,
'Embedder Callback': group.scripting,
'Evaluate Script': group.scripting,
'Event': group.scripting,
'Animation Frame Fired': group.scripting,
'Fire Idle Callback': group.scripting,
'Function Call': group.scripting,
'DOM GC': group.scriptGC,
'GC Event': group.scriptGC,
'GPU': group.gpu,
'Hit Test': group.compositing,
'Invalidate Layout': group.styleLayout,
'JS Frame': group.scripting,
'Input Latency': group.scripting,
'Layout': group.styleLayout,
'Major GC': group.scriptGC,
'DOMContentLoaded event': group.scripting,
'First paint': group.painting,
'FMP': group.painting,
'FMP candidate': group.painting,
'Load event': group.scripting,
'Minor GC': group.scriptGC,
'Paint': group.painting,
'Paint Image': group.images,
'Paint Setup': group.painting,
'Parse Stylesheet': group.parseHTML,
'Parse HTML': group.parseHTML,
'Parse Script': group.scriptParseCompile,
'Other': group.other,
'Rasterize Paint': group.painting,
'Recalculate Style': group.styleLayout,
'Request Animation Frame': group.scripting,
'Request Idle Callback': group.scripting,
'Request Main Thread Frame': group.painting,
'Image Resize': group.images,
'Finish Loading': group.loading,
'Receive Data': group.loading,
'Receive Response': group.loading,
'Send Request': group.loading,
'Run Microtasks': group.scripting,
'Schedule Style Recalculation': group.styleLayout,
'Scroll': group.compositing,
'Task': group.other,
'Timer Fired': group.scripting,
'Install Timer': group.scripting,
'Remove Timer': group.scripting,
'Timestamp': group.scripting,
'Update Layer': group.compositing,
'Update Layer Tree': group.compositing,
'User Timing': group.scripting,
'Create WebSocket': group.scripting,
'Destroy WebSocket': group.scripting,
'Receive WebSocket Handshake': group.scripting,
'Send WebSocket Handshake': group.scripting,
'XHR Load': group.scripting,
'XHR Ready State Change': group.scripting,
};

and we'll want any children with events of name in the 3 script*categories. (also note: many of those task names are for instant events so they have 0 duration. ;)

Copy link
Member

Choose a reason for hiding this comment

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

we can exclude GC from the report for now but might as well collect it here and put in extInfo ? or ignore it and leave a comment saying so..

@paulirish
Copy link
Member

Also when scripts are not moved to v8.parseOnBackground than I can't seem to find any parse event.

perhaps they are on a different thread? (tid)? that would usually exclude them.

@wardpeet
Copy link
Collaborator Author

perhaps they are on a different thread? (tid)? that would usually exclude them.
yes indeed, not on the main thread which is good but should I also try to include it in the report?

Not all scripts are parsed on the parser thread (only the once that have async/defer). When they're not it seems like there is no parse time registered in the trace, it's probably included in compile time of the script?

const tableDetails = BootupTime.makeTableDetails(headings, results);

return {
score: totalBootupTime < 4000,
Copy link
Collaborator

Choose a reason for hiding this comment

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

4s is quite generous, did we consider using a numeric instead of binary score to allow nuance?

* @return {!Map<string, Number>}
*/
static getExecutionTimingsByURL(trace) {
const timelineModel = new DevtoolsTimelineModel(trace);
Copy link
Collaborator

Choose a reason for hiding this comment

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

are we doing this in a lot of places/should it be a computed artifact?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Are you referring to make a computed artifact requestExecutionTimingsByUrl(trace) or rather have a computed artifact requestExecutionTimings(trace, 'URL')

Copy link
Member

@paulirish paulirish Oct 20, 2017

Choose a reason for hiding this comment

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

the idea is to make DTM a computed artifact.

we were also discussing doing a deepFreeze (from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) on the trace data before sending it into devtools.

so it wont mutate.

if there are problems with this we could just deepFreeze each event in the traceEvents array. should be fine.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

should i do it in this PR? or just a followup?

Copy link
Member

Choose a reason for hiding this comment

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

filed here #3702

const WebInspector = require('../lib/web-inspector');
const Util = require('../report/v2/renderer/util.js');

const group = {
Copy link
Member

Choose a reason for hiding this comment

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

could pull these two objects into a lib/traces/trace-event-groups.js or something so that they can be used across audits

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.

spoiler alert: I implemented all my suggestions in #3703

wdyt? can you review?

one of the major things is only showing the scripting categories, not every kind of task we can associate with the scriptURL. that means our header array is now fixed.

assert.equal(Math.round(output.rawValue), 176);

const valueOf = name => output.extendedInfo.value[name];
assert.deepEqual(valueOf('https://www.google-analytics.com/analytics.js'), {'Evaluate Script': 40.1, 'Compile Script': 9.6, 'Recalculate Style': 0.2});
Copy link
Member

Choose a reason for hiding this comment

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

It was kind of confusing that our extendedInfo reported tasks, but the UI reported taskGroups. I don't have a strong opinion on what should be exposed in extendedInfo. Maybe we just leave extendedInfo empty this time? In the tests we could assert against the table details instead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it might be confusing indeed but isn't it handy to show the raw information somewhere in the json report so people who want to use it can use it? Or isn't that why extendedInfo exists?

];

// Group tasks per url
const groupsPerUrl = Array.from(bootupTimings).map(([url, durations]) => {
Copy link
Member

Choose a reason for hiding this comment

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

this loop groups individual tasks into taskGroups. I figured this could be a lot simpler if we just do the same thing up inside the inner getExecutionTimingsByURL loop. that's what you'll see in #3703

perUrlNode.children.forEach((perTaskPerUrlNode) => {
const taskGroup = WebInspector.TimelineUIUtils.eventStyle(perTaskPerUrlNode.event);
tasks[taskGroup.title] = tasks[taskGroup.title] || 0;
tasks[taskGroup.title] += Number((perTaskPerUrlNode.selfTime || 0).toFixed(1));
Copy link
Member

Choose a reason for hiding this comment

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

i felt weird about this rounding here as we're summing, so i moved it to outside of the loop.

*/
static getExecutionTimingsByURL(trace) {
const timelineModel = new DevtoolsTimelineModel(trace);
const bottomUpByName = timelineModel.bottomUpGroupBy('URL');
Copy link
Member

Choose a reason for hiding this comment

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

bottomUpByURL

@paulirish
Copy link
Member

I own landing this now. No more work needed from you, @wardpeet. :)

Still need a little more work on #3703 then we're good with both.

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.

6 participants