diff --git a/lighthouse-core/audits/bootup-time.js b/lighthouse-core/audits/bootup-time.js index 6c6dbff51cf3..f19ecd0859d2 100644 --- a/lighthouse-core/audits/bootup-time.js +++ b/lighthouse-core/audits/bootup-time.js @@ -9,7 +9,6 @@ const Audit = require('./audit'); const WebInspector = require('../lib/web-inspector'); const Util = require('../report/html/renderer/util'); const {groupIdToName, taskToGroup} = require('../lib/task-groups'); -const THRESHOLD_IN_MS = 10; class BootupTime extends Audit { /** @@ -29,7 +28,7 @@ class BootupTime extends Audit { } /** - * @return {LH.Audit.ScoreOptions} + * @return {LH.Audit.ScoreOptions & {thresholdInMs: number}} */ static get defaultOptions() { return { @@ -37,6 +36,7 @@ class BootupTime extends Audit { // <500ms ~= 100, >2s is yellow, >3.5s is red scorePODR: 600, scoreMedian: 3500, + thresholdInMs: 50, }; } @@ -78,6 +78,7 @@ class BootupTime extends Audit { * @return {Promise} */ static async audit(artifacts, context) { + const settings = context.settings || {}; const trace = artifacts.traces[BootupTime.DEFAULT_PASS]; const devtoolsTimelineModel = await artifacts.requestDevtoolsTimelineModel(trace); const executionTimings = BootupTime.getExecutionTimingsByURL(devtoolsTimelineModel); @@ -87,15 +88,22 @@ class BootupTime extends Audit { const headings = [ {key: 'url', itemType: 'url', text: 'URL'}, - {key: 'scripting', itemType: 'text', text: groupIdToName.scripting}, - {key: 'scriptParseCompile', itemType: 'text', text: groupIdToName.scriptParseCompile}, + {key: 'scripting', granularity: 1, itemType: 'ms', text: groupIdToName.scripting}, + {key: 'scriptParseCompile', granularity: 1, itemType: 'ms', + text: groupIdToName.scriptParseCompile}, ]; + const multiplier = settings.throttlingMethod === 'simulate' ? + settings.throttling.cpuSlowdownMultiplier : 1; // 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); + for (const [name, value] of Object.entries(groups)) { + groups[name] = value * multiplier; + totalBootupTime += value * multiplier; + } + extendedInfo[url] = groups; const scriptingTotal = groups[groupIdToName.scripting] || 0; @@ -105,11 +113,11 @@ class BootupTime extends Audit { 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), + scripting: scriptingTotal, + scriptParseCompile: parseCompileTotal, }; }) - .filter(result => result.sum >= THRESHOLD_IN_MS) + .filter(result => result.sum >= context.options.thresholdInMs) .sort((a, b) => b.sum - a.sum); const summary = {wastedMs: totalBootupTime}; diff --git a/lighthouse-core/audits/mainthread-work-breakdown.js b/lighthouse-core/audits/mainthread-work-breakdown.js index 70518693a50a..3ea3b14d3793 100644 --- a/lighthouse-core/audits/mainthread-work-breakdown.js +++ b/lighthouse-core/audits/mainthread-work-breakdown.js @@ -62,6 +62,7 @@ class MainThreadWorkBreakdown extends Audit { * @return {Promise} */ static async audit(artifacts, context) { + const settings = context.settings || {}; const trace = artifacts.traces[MainThreadWorkBreakdown.DEFAULT_PASS]; const devtoolsTimelineModel = await artifacts.requestDevtoolsTimelineModel(trace); @@ -70,9 +71,13 @@ class MainThreadWorkBreakdown extends Audit { ); let totalExecutionTime = 0; + const multiplier = settings.throttlingMethod === 'simulate' ? + settings.throttling.cpuSlowdownMultiplier : 1; + const extendedInfo = {}; const categoryTotals = {}; const results = Array.from(executionTimings).map(([eventName, duration]) => { + duration *= multiplier; totalExecutionTime += duration; extendedInfo[eventName] = duration; const groupName = taskToGroup[eventName]; @@ -83,14 +88,14 @@ class MainThreadWorkBreakdown extends Audit { return { category: eventName, group: groupName, - duration: Util.formatMilliseconds(duration, 1), + duration: duration, }; }); const headings = [ {key: 'group', itemType: 'text', text: 'Category'}, {key: 'category', itemType: 'text', text: 'Work'}, - {key: 'duration', itemType: 'text', text: 'Time spent'}, + {key: 'duration', itemType: 'ms', granularity: 1, text: 'Time spent'}, ]; // @ts-ignore - stableSort added to Array by WebInspector results.stableSort((a, b) => categoryTotals[b.group] - categoryTotals[a.group]); @@ -105,7 +110,7 @@ class MainThreadWorkBreakdown extends Audit { return { score, rawValue: totalExecutionTime, - displayValue: ['%d\xa0ms', totalExecutionTime], + displayValue: [Util.MS_DISPLAY_VALUE, totalExecutionTime], details: tableDetails, extendedInfo: { value: extendedInfo, diff --git a/lighthouse-core/test/audits/bootup-time-test.js b/lighthouse-core/test/audits/bootup-time-test.js index 60e27a46485c..4cf4797262da 100644 --- a/lighthouse-core/test/audits/bootup-time-test.js +++ b/lighthouse-core/test/audits/bootup-time-test.js @@ -15,6 +15,14 @@ const acceptableTrace = require('../fixtures/traces/progressive-app-m60.json'); const errorTrace = require('../fixtures/traces/airhorner_no_fcp.json'); describe('Performance: bootup-time audit', () => { + const auditOptions = Object.assign({}, BootupTime.defaultOptions, {thresholdInMs: 10}); + const roundedValueOf = (output, name) => { + const value = output.extendedInfo.value[name]; + const roundedValue = {}; + Object.keys(value).forEach(key => roundedValue[key] = Math.round(value[key] * 10) / 10); + return roundedValue; + }; + it('should compute the correct BootupTime values', () => { const artifacts = Object.assign({ traces: { @@ -22,28 +30,37 @@ describe('Performance: bootup-time audit', () => { }, }, Runner.instantiateComputedArtifacts()); - return BootupTime.audit(artifacts, {options: BootupTime.defaultOptions}).then(output => { + return BootupTime.audit(artifacts, {options: auditOptions}).then(output => { assert.equal(output.details.items.length, 4); assert.equal(output.score, 1); assert.equal(Math.round(output.rawValue), 176); - const roundedValueOf = name => { - const value = output.extendedInfo.value[name]; - const roundedValue = {}; - Object.keys(value).forEach(key => roundedValue[key] = Math.round(value[key] * 10) / 10); - return roundedValue; - }; - - assert.deepEqual(roundedValueOf('https://pwa.rocks/script.js'), {[groupIdToName.scripting]: 31.8, [groupIdToName.styleLayout]: 5.5, [groupIdToName.scriptParseCompile]: 1.3}); - assert.deepEqual(roundedValueOf('https://www.googletagmanager.com/gtm.js?id=GTM-Q5SW'), {[groupIdToName.scripting]: 25, [groupIdToName.scriptParseCompile]: 5.5, [groupIdToName.styleLayout]: 1.2}); - assert.deepEqual(roundedValueOf('https://www.google-analytics.com/plugins/ua/linkid.js'), {[groupIdToName.scripting]: 25.2, [groupIdToName.scriptParseCompile]: 1.2}); - assert.deepEqual(roundedValueOf('https://www.google-analytics.com/analytics.js'), {[groupIdToName.scripting]: 40.1, [groupIdToName.scriptParseCompile]: 9.6, [groupIdToName.styleLayout]: 0.2}); + assert.deepEqual(roundedValueOf(output, 'https://pwa.rocks/script.js'), {[groupIdToName.scripting]: 31.8, [groupIdToName.styleLayout]: 5.5, [groupIdToName.scriptParseCompile]: 1.3}); + assert.deepEqual(roundedValueOf(output, 'https://www.googletagmanager.com/gtm.js?id=GTM-Q5SW'), {[groupIdToName.scripting]: 25, [groupIdToName.scriptParseCompile]: 5.5, [groupIdToName.styleLayout]: 1.2}); + assert.deepEqual(roundedValueOf(output, 'https://www.google-analytics.com/plugins/ua/linkid.js'), {[groupIdToName.scripting]: 25.2, [groupIdToName.scriptParseCompile]: 1.2}); + assert.deepEqual(roundedValueOf(output, 'https://www.google-analytics.com/analytics.js'), {[groupIdToName.scripting]: 40.1, [groupIdToName.scriptParseCompile]: 9.6, [groupIdToName.styleLayout]: 0.2}); assert.ok(output.details.items.length < Object.keys(output.extendedInfo.value).length, - 'Items below 10ms threshold were not filtered out'); + 'Items below threshold were not filtered out'); }); }).timeout(10000); + it('should compute the correct values when simulated', async () => { + const artifacts = Object.assign({ + traces: {defaultPass: acceptableTrace}, + }, Runner.instantiateComputedArtifacts()); + + const options = auditOptions; + const settings = {throttlingMethod: 'simulate', throttling: {cpuSlowdownMultiplier: 3}}; + const output = await BootupTime.audit(artifacts, {options, settings}); + + assert.equal(output.details.items.length, 7); + assert.equal(output.score, 0.99); + assert.equal(Math.round(output.rawValue), 528); + + assert.deepEqual(roundedValueOf(output, 'https://pwa.rocks/script.js'), {[groupIdToName.scripting]: 95.3, [groupIdToName.styleLayout]: 16.4, [groupIdToName.scriptParseCompile]: 3.9}); + }); + it('should get no data when no events are present', () => { const artifacts = Object.assign({ traces: { @@ -51,7 +68,7 @@ describe('Performance: bootup-time audit', () => { }, }, Runner.instantiateComputedArtifacts()); - return BootupTime.audit(artifacts, {options: BootupTime.defaultOptions}) + return BootupTime.audit(artifacts, {options: auditOptions}) .then(output => { assert.equal(output.details.items.length, 0); assert.equal(output.score, 1); diff --git a/lighthouse-core/test/audits/mainthread-work-breakdown-test.js b/lighthouse-core/test/audits/mainthread-work-breakdown-test.js index e751feb3c26c..764afa798ff0 100644 --- a/lighthouse-core/test/audits/mainthread-work-breakdown-test.js +++ b/lighthouse-core/test/audits/mainthread-work-breakdown-test.js @@ -83,6 +83,29 @@ describe('Performance: page execution timings audit', () => { }); }); + it('should compute the correct values when simulated', async () => { + const artifacts = Object.assign({ + traces: {defaultPass: acceptableTrace}, + }, Runner.instantiateComputedArtifacts()); + + const settings = {throttlingMethod: 'simulate', throttling: {cpuSlowdownMultiplier: 3}}; + const output = await PageExecutionTimings.audit(artifacts, {options, settings}); + const valueOf = name => Math.round(output.extendedInfo.value[name]); + + assert.equal(output.details.items.length, 12); + assert.equal(output.score, 0.93); + assert.equal(Math.round(output.rawValue), 1832); + + for (const category in output.extendedInfo.value) { + if (output.extendedInfo.value[category]) { + assert.ok( + Math.abs(valueOf(category) - 3 * acceptableTraceExpectations[category]) < 2, + 'should have multiplied value by slowdown multiplier' + ); + } + } + }); + it('should compute the correct pageExecutionTiming values for the redirect trace', () => { const artifacts = Object.assign({ traces: { diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json index e00451e5da4d..c549796a7171 100644 --- a/lighthouse-core/test/results/sample_v2.json +++ b/lighthouse-core/test/results/sample_v2.json @@ -870,7 +870,7 @@ "mainthread-work-breakdown": { "score": 0.98, "displayValue": [ - "%d ms", + "%10d ms", 1359.7759999930859 ], "rawValue": 1359.7759999930859, @@ -911,7 +911,8 @@ }, { "key": "duration", - "itemType": "text", + "itemType": "ms", + "granularity": 1, "text": "Time spent" } ], @@ -919,72 +920,72 @@ { "category": "Evaluate Script", "group": "Script Evaluation", - "duration": "1,121 ms" + "duration": 1121.2470000088215 }, { "category": "Run Microtasks", "group": "Script Evaluation", - "duration": "7 ms" + "duration": 6.680999994277954 }, { "category": "XHR Ready State Change", "group": "Script Evaluation", - "duration": "0 ms" + "duration": 0.026999980211257935 }, { "category": "XHR Load", "group": "Script Evaluation", - "duration": "0 ms" + "duration": 0.011000007390975952 }, { "category": "Layout", "group": "Style & Layout", - "duration": "89 ms" + "duration": 89.00499999523163 }, { "category": "Recalculate Style", "group": "Style & Layout", - "duration": "33 ms" + "duration": 32.57899996638298 }, { "category": "Parse HTML", "group": "Parsing HTML & CSS", - "duration": "63 ms" + "duration": 63.04200002551079 }, { "category": "Parse Stylesheet", "group": "Parsing HTML & CSS", - "duration": "1 ms" + "duration": 1.3200000524520874 }, { "category": "Minor GC", "group": "Garbage collection", - "duration": "20 ms" + "duration": 19.94599997997284 }, { "category": "DOM GC", "group": "Garbage collection", - "duration": "6 ms" + "duration": 6.252999991178513 }, { "category": "Compile Script", "group": "Script Parsing & Compile", - "duration": "8 ms" + "duration": 7.7519999742507935 }, { "category": "Update Layer Tree", "group": "Compositing", - "duration": "4 ms" + "duration": 4.238999992609024 }, { "category": "Composite Layers", "group": "Compositing", - "duration": "3 ms" + "duration": 2.732000023126602 }, { "category": "Paint", "group": "Paint", - "duration": "5 ms" + "duration": 4.94200000166893 } ] } @@ -1033,12 +1034,14 @@ }, { "key": "scripting", - "itemType": "text", + "granularity": 1, + "itemType": "ms", "text": "Script Evaluation" }, { "key": "scriptParseCompile", - "itemType": "text", + "granularity": 1, + "itemType": "ms", "text": "Script Parsing & Compile" } ], @@ -1046,20 +1049,20 @@ { "url": "http://localhost:10200/dobetterweb/dbw_tester.html", "sum": 957.277999997139, - "scripting": "955 ms", - "scriptParseCompile": "3 ms" + "scripting": 954.5010000169277, + "scriptParseCompile": 2.776999980211258 }, { "url": "http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js", "sum": 81.23699998855591, - "scripting": "80 ms", - "scriptParseCompile": "1 ms" + "scripting": 80.03200000524521, + "scriptParseCompile": 1.2049999833106995 }, { "url": "http://localhost:10200/zone.js", "sum": 78.23800000548363, - "scripting": "77 ms", - "scriptParseCompile": "2 ms" + "scripting": 76.56400001049042, + "scriptParseCompile": 1.6739999949932098 } ], "summary": {