-
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
Measure and visualize perf metrics #1936
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"passes": [{ | ||
"recordNetwork": true, | ||
"recordTrace": true, | ||
"pauseBeforeTraceEndMs": 5000, | ||
"useThrottling": true, | ||
"gatherers": [] | ||
}], | ||
|
||
"audits": [ | ||
"first-meaningful-paint", | ||
"speed-index-metric", | ||
"estimated-input-latency", | ||
"time-to-interactive" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,22 @@ | |
|
||
const log = require('../../../lighthouse-core/lib/log.js'); | ||
|
||
/** | ||
* @param {!Object} object | ||
* @param {string} path | ||
* @return {*} | ||
*/ | ||
function safeGet(object, path) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not wild about this, mostly because it completely disables typing. Is the issue that the intermediate objects are sometimes undefined? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right - this happens when the audit fails. An alternative is to always to have the intermediate objects be created even when the audit fails. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think it's reasonable not to have an I'm sighing via code review because I'm currently in the process of re-enabling closure compiler for the codebase and this will all have to be undone again :) I don't want to block |
||
const components = path.split('.'); | ||
for (const component of components) { | ||
if (!object) { | ||
return null; | ||
} | ||
object = object[component]; | ||
} | ||
return object; | ||
} | ||
|
||
class Metrics { | ||
constructor(traceEvents, auditResults) { | ||
this._traceEvents = traceEvents; | ||
|
@@ -35,81 +51,145 @@ class Metrics { | |
id: 'navstart', | ||
getTs: auditResults => { | ||
const fmpExt = auditResults['first-meaningful-paint'].extendedInfo; | ||
return fmpExt.value.timestamps.navStart; | ||
return safeGet(fmpExt, 'value.timestamps.navStart'); | ||
}, | ||
getTiming: auditResults => { | ||
const fmpExt = auditResults['first-meaningful-paint'].extendedInfo; | ||
return safeGet(fmpExt, 'value.timings.navStart'); | ||
} | ||
}, | ||
{ | ||
name: 'First Contentful Paint', | ||
id: 'ttfcp', | ||
getTs: auditResults => { | ||
const fmpExt = auditResults['first-meaningful-paint'].extendedInfo; | ||
return fmpExt.value.timestamps.fCP; | ||
return safeGet(fmpExt, 'value.timestamps.fCP'); | ||
}, | ||
getTiming: auditResults => { | ||
const fmpExt = auditResults['first-meaningful-paint'].extendedInfo; | ||
return safeGet(fmpExt, 'value.timings.fCP'); | ||
} | ||
}, | ||
{ | ||
name: 'First Meaningful Paint', | ||
id: 'ttfmp', | ||
getTs: auditResults => { | ||
const fmpExt = auditResults['first-meaningful-paint'].extendedInfo; | ||
return fmpExt.value.timestamps.fMP; | ||
return safeGet(fmpExt, 'value.timestamps.fMP'); | ||
}, | ||
getTiming: auditResults => { | ||
const fmpExt = auditResults['first-meaningful-paint'].extendedInfo; | ||
return safeGet(fmpExt, 'value.timings.fMP'); | ||
} | ||
}, | ||
{ | ||
name: 'Perceptual Speed Index', | ||
id: 'psi', | ||
getTs: auditResults => { | ||
const siExt = auditResults['speed-index-metric'].extendedInfo; | ||
return siExt.value.timestamps.perceptualSpeedIndex; | ||
return safeGet(siExt, 'value.timestamps.perceptualSpeedIndex'); | ||
}, | ||
getTiming: auditResults => { | ||
const siExt = auditResults['speed-index-metric'].extendedInfo; | ||
return safeGet(siExt, 'value.timings.perceptualSpeedIndex'); | ||
} | ||
}, | ||
{ | ||
name: 'First Visual Change', | ||
id: 'fv', | ||
getTs: auditResults => { | ||
const siExt = auditResults['speed-index-metric'].extendedInfo; | ||
return siExt.value.timestamps.firstVisualChange; | ||
return safeGet(siExt, 'value.timestamps.firstVisualChange'); | ||
}, | ||
getTiming: auditResults => { | ||
const siExt = auditResults['speed-index-metric'].extendedInfo; | ||
return safeGet(siExt, 'value.timings.firstVisualChange'); | ||
} | ||
}, | ||
{ | ||
name: 'Visually Complete 85%', | ||
id: 'vc85', | ||
getTs: auditResults => { | ||
const siExt = auditResults['time-to-interactive'].extendedInfo; | ||
return siExt.value.timestamps.visuallyReady; | ||
return safeGet(siExt, 'value.timestamps.visuallyReady'); | ||
}, | ||
getTiming: auditResults => { | ||
const siExt = auditResults['time-to-interactive'].extendedInfo; | ||
return safeGet(siExt, 'value.timings.visuallyReady'); | ||
} | ||
}, | ||
{ | ||
name: 'Visually Complete 100%', | ||
id: 'vc100', | ||
getTs: auditResults => { | ||
const siExt = auditResults['speed-index-metric'].extendedInfo; | ||
return siExt.value.timestamps.visuallyComplete; | ||
return safeGet(siExt, 'value.timestamps.visuallyComplete'); | ||
}, | ||
getTiming: auditResults => { | ||
const siExt = auditResults['speed-index-metric'].extendedInfo; | ||
return safeGet(siExt, 'value.timings.visuallyComplete'); | ||
} | ||
}, | ||
{ | ||
name: 'Time to Interactive (vAlpha)', | ||
id: 'tti', | ||
getTs: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return ttiExt.value.timestamps.timeToInteractive; | ||
return safeGet(ttiExt, 'value.timestamps.timeToInteractive'); | ||
}, | ||
getTiming: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return safeGet(ttiExt, 'value.timings.timeToInteractive'); | ||
} | ||
}, | ||
{ | ||
name: 'Time to Interactive (vAlpha non-visual)', | ||
id: 'tti-non-visual', | ||
getTs: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return ttiExt.value.timestamps.timeToInteractiveB; | ||
return safeGet(ttiExt, 'value.timestamps.timeToInteractiveB'); | ||
}, | ||
getTiming: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return safeGet(ttiExt, 'value.timings.timeToInteractiveB'); | ||
} | ||
}, | ||
{ | ||
name: 'Time to Interactive (vAlpha non-visual, 5s)', | ||
id: 'tti-non-visual-5s', | ||
getTs: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return ttiExt.value.timestamps.timeToInteractiveC; | ||
return safeGet(ttiExt, 'value.timestamps.timeToInteractiveC'); | ||
}, | ||
getTiming: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return safeGet(ttiExt, 'value.timings.timeToInteractiveC'); | ||
} | ||
}, | ||
{ | ||
name: 'End of Trace', | ||
id: 'eot', | ||
getTs: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return safeGet(ttiExt, 'value.timestamps.endOfTrace'); | ||
}, | ||
getTiming: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return safeGet(ttiExt, 'value.timings.endOfTrace'); | ||
} | ||
}, | ||
{ | ||
name: 'On Load', | ||
id: 'onload', | ||
getTs: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return safeGet(ttiExt, 'value.timestamps.onLoad'); | ||
}, | ||
getTiming: auditResults => { | ||
const ttiExt = auditResults['time-to-interactive'].extendedInfo; | ||
return safeGet(ttiExt, 'value.timings.onLoad'); | ||
} | ||
} | ||
]; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Lighthouse Metrics Analysis | ||
|
||
For context and roadmap, please see issue: | ||
https://github.com/GoogleChrome/lighthouse/issues/1924 | ||
|
||
## Workflow | ||
|
||
### Setup | ||
|
||
You need to build lighthouse first. | ||
|
||
### Commands | ||
|
||
``` | ||
# View all commands | ||
$ cd plots | ||
$ npm run | ||
|
||
# Run lighthouse to collect metrics data | ||
$ npm run measure | ||
|
||
# Analyze the data to generate a summary file (i.e. out/generatedResults.js) | ||
$ npm run analyze | ||
|
||
# View visualization | ||
# Open index.html in browser | ||
``` |
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.
should add this to
.npmignore
as well. Also, do we want to ship plots with the npm module? Could be useful, but we might want to wait on that, so we could npmignoreplots/
entirely for nowThere 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.
Let's npmignore plots entirely for now. Once it's been more thoroughly tested and documented, then it makes sense to ship it with the npm module.
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.
SGTM