Skip to content

Commit

Permalink
core(devtools-timeline-model): extract model generation to a computed…
Browse files Browse the repository at this point in the history
… artifact...

Assert it doesn't mutate the trace, with a few exceptions. Extract timeline task-groups into lib.
  • Loading branch information
wardpeet authored and paulirish committed Dec 1, 2017
1 parent 3bdb56e commit 46f6d2a
Show file tree
Hide file tree
Showing 11 changed files with 399 additions and 358 deletions.
162 changes: 40 additions & 122 deletions lighthouse-core/audits/bootup-time.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,91 +6,9 @@
'use strict';

const Audit = require('./audit');
const DevtoolsTimelineModel = require('../lib/traces/devtools-timeline-model');
const WebInspector = require('../lib/web-inspector');
const Util = require('../report/v2/renderer/util.js');

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,
};
const Util = require('../report/v2/renderer/util');
const {groupIdToName, taskToGroup} = require('../lib/task-groups');

class BootupTime extends Audit {
/**
Expand All @@ -109,11 +27,10 @@ class BootupTime extends Audit {
}

/**
* @param {!Array<TraceEvent>=} trace
* @param {DevtoolsTimelineModel} timelineModel
* @return {!Map<string, Number>}
*/
static getExecutionTimingsByURL(trace) {
const timelineModel = new DevtoolsTimelineModel(trace);
static getExecutionTimingsByURL(timelineModel) {
const bottomUpByURL = timelineModel.bottomUpGroupBy('URL');
const result = new Map();

Expand All @@ -128,7 +45,7 @@ class BootupTime extends Audit {
// eventStyle() returns a string like 'Evaluate Script'
const task = WebInspector.TimelineUIUtils.eventStyle(perTaskPerUrlNode.event);
// Resolve which taskGroup we're using
const groupName = taskToGroup[task.title] || group.other;
const groupName = taskToGroup[task.title] || groupIdToName.other;
const groupTotal = taskGroups[groupName] || 0;
taskGroups[groupName] = groupTotal + (perTaskPerUrlNode.selfTime || 0);
});
Expand All @@ -144,46 +61,47 @@ class BootupTime extends Audit {
*/
static audit(artifacts) {
const trace = artifacts.traces[BootupTime.DEFAULT_PASS];
const executionTimings = BootupTime.getExecutionTimingsByURL(trace);
return artifacts.requestDevtoolsTimelineModel(trace).then(devtoolsTimelineModel => {
const executionTimings = BootupTime.getExecutionTimingsByURL(devtoolsTimelineModel);
let totalBootupTime = 0;
const extendedInfo = {};

const headings = [
{key: 'url', itemType: 'url', text: 'URL'},
{key: 'scripting', itemType: 'text', text: groupIdToName.scripting},
{key: 'scriptParseCompile', itemType: 'text', text: groupIdToName.scriptParseCompile},
];

let totalBootupTime = 0;
const extendedInfo = {};
// map data in correct format to create a table
const results = Array.from(executionTimings).map(([url, groups]) => {
// Add up the totalBootupTime for all the taskGroups
totalBootupTime += Object.keys(groups).reduce((sum, name) => sum += groups[name], 0);
extendedInfo[url] = groups;

const headings = [
{key: 'url', itemType: 'url', text: 'URL'},
{key: 'scripting', itemType: 'text', text: group.scripting},
{key: 'scriptParseCompile', itemType: 'text', text: group.scriptParseCompile},
];
const scriptingTotal = groups[groupIdToName.scripting] || 0;
const parseCompileTotal = groups[groupIdToName.scriptParseCompile] || 0;
return {
url: url,
sum: scriptingTotal + parseCompileTotal,
// Only reveal the javascript task costs
// Later we can account for forced layout costs, etc.
scripting: Util.formatMilliseconds(scriptingTotal, 1),
scriptParseCompile: Util.formatMilliseconds(parseCompileTotal, 1),
};
}).sort((a, b) => b.sum - a.sum);

// map data in correct format to create a table
const results = Array.from(executionTimings).map(([url, groups]) => {
// Add up the totalBootupTime for all the taskGroups
totalBootupTime += Object.keys(groups).reduce((sum, name) => sum += groups[name], 0);
extendedInfo[url] = groups;
const tableDetails = BootupTime.makeTableDetails(headings, results);

const scriptingTotal = groups[group.scripting] || 0;
const parseCompileTotal = groups[group.scriptParseCompile] || 0;
return {
url: url,
sum: scriptingTotal + parseCompileTotal,
// Only reveal the javascript task costs
// Later we can account for forced layout costs, etc.
scripting: Util.formatMilliseconds(scriptingTotal, 1),
scriptParseCompile: Util.formatMilliseconds(parseCompileTotal, 1),
score: totalBootupTime < 4000,
rawValue: totalBootupTime,
displayValue: Util.formatMilliseconds(totalBootupTime),
details: tableDetails,
extendedInfo: {
value: extendedInfo,
},
};
}).sort((a, b) => b.sum - a.sum);

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

return {
score: totalBootupTime < 4000,
rawValue: totalBootupTime,
displayValue: Util.formatMilliseconds(totalBootupTime),
details: tableDetails,
extendedInfo: {
value: extendedInfo,
},
};
});
}
}

Expand Down
168 changes: 45 additions & 123 deletions lighthouse-core/audits/mainthread-work-breakdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,91 +12,8 @@

const Audit = require('./audit');
const Util = require('../report/v2/renderer/util');
const DevtoolsTimelineModel = require('../lib/traces/devtools-timeline-model');

// We group all trace events into groups to show a highlevel breakdown of the page
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,
};
const {taskToGroup} = require('../lib/task-groups');

class PageExecutionTimings extends Audit {
/**
Expand All @@ -115,11 +32,10 @@ class PageExecutionTimings extends Audit {
}

/**
* @param {!Array<TraceEvent>} trace
* @param {!DevtoolsTimelineModel} timelineModel
* @return {!Map<string, number>}
*/
static getExecutionTimingsByCategory(trace) {
const timelineModel = new DevtoolsTimelineModel(trace);
static getExecutionTimingsByCategory(timelineModel) {
const bottomUpByName = timelineModel.bottomUpGroupBy('EventName');

const result = new Map();
Expand All @@ -135,43 +51,49 @@ class PageExecutionTimings extends Audit {
*/
static audit(artifacts) {
const trace = artifacts.traces[PageExecutionTimings.DEFAULT_PASS];
const executionTimings = PageExecutionTimings.getExecutionTimingsByCategory(trace);
let totalExecutionTime = 0;

const extendedInfo = {};
const categoryTotals = {};
const results = Array.from(executionTimings).map(([eventName, duration]) => {
totalExecutionTime += duration;
extendedInfo[eventName] = duration;
const groupName = taskToGroup[eventName];

const categoryTotal = categoryTotals[groupName] || 0;
categoryTotals[groupName] = categoryTotal + duration;

return {
category: eventName,
group: groupName,
duration: Util.formatMilliseconds(duration, 1),
};
});

const headings = [
{key: 'group', itemType: 'text', text: 'Category'},
{key: 'category', itemType: 'text', text: 'Work'},
{key: 'duration', itemType: 'text', text: 'Time spent'},
];
results.stableSort((a, b) => categoryTotals[b.group] - categoryTotals[a.group]);
const tableDetails = PageExecutionTimings.makeTableDetails(headings, results);

return {
score: false,
rawValue: totalExecutionTime,
displayValue: Util.formatMilliseconds(totalExecutionTime),
details: tableDetails,
extendedInfo: {
value: extendedInfo,
},
};
return artifacts.requestDevtoolsTimelineModel(trace)
.then(devtoolsTimelineModel => {
const executionTimings = PageExecutionTimings.getExecutionTimingsByCategory(
devtoolsTimelineModel
);
let totalExecutionTime = 0;

const extendedInfo = {};
const categoryTotals = {};
const results = Array.from(executionTimings).map(([eventName, duration]) => {
totalExecutionTime += duration;
extendedInfo[eventName] = duration;
const groupName = taskToGroup[eventName];

const categoryTotal = categoryTotals[groupName] || 0;
categoryTotals[groupName] = categoryTotal + duration;

return {
category: eventName,
group: groupName,
duration: Util.formatMilliseconds(duration, 1),
};
});

const headings = [
{key: 'group', itemType: 'text', text: 'Category'},
{key: 'category', itemType: 'text', text: 'Work'},
{key: 'duration', itemType: 'text', text: 'Time spent'},
];
results.stableSort((a, b) => categoryTotals[b.group] - categoryTotals[a.group]);
const tableDetails = PageExecutionTimings.makeTableDetails(headings, results);

return {
score: false,
rawValue: totalExecutionTime,
displayValue: Util.formatMilliseconds(totalExecutionTime),
details: tableDetails,
extendedInfo: {
value: extendedInfo,
},
};
});
}
}

Expand Down
Loading

0 comments on commit 46f6d2a

Please sign in to comment.