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

core(interactive): also use LCP as bounds in observed metric #16173

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/audits/metrics/interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);

/**
* @fileoverview This audit identifies the time the page is "consistently interactive".
* Looks for the first period of at least 5 seconds after FCP where both CPU and network were quiet,
* Looks for the first period of at least 5 seconds after LCP where both CPU and network were quiet,
* and returns the timestamp of the beginning of the CPU quiet period.
* @see https://docs.google.com/document/d/1GGiI9-7KeY3TPqS3YT271upUVimo-XiL5mwWorDUD4c/edit#
*/
Expand Down
6 changes: 4 additions & 2 deletions core/computed/metrics/interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,13 @@ class Interactive extends NavigationMetric {
* @return {{cpuQuietPeriod: TimePeriod, networkQuietPeriod: TimePeriod, cpuQuietPeriods: Array<TimePeriod>, networkQuietPeriods: Array<TimePeriod>}}
*/
static findOverlappingQuietPeriods(longTasks, networkRecords, processedNavigation) {
const FcpTsInMs = processedNavigation.timestamps.firstContentfulPaint / 1000;
const minTime = processedNavigation.timestamps.largestContentfulPaint !== undefined ?
processedNavigation.timestamps.largestContentfulPaint / 1000 :
processedNavigation.timestamps.firstContentfulPaint / 1000;
Comment on lines +89 to +91
Copy link
Member

Choose a reason for hiding this comment

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

Not sure if it matters since this is TTI we're talking about (but that applies to making any changes at all, so... :), but seems bad to have this fallback? Makes the metric harder to reason about, discontinuous for an otherwise identical trace depending on if LCP was captured or not, and diverges from the lantern version, which throws if there was no LCP.

Choose a reason for hiding this comment

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

I agree that the fallback in TTI can introduce inconsistencies. While it might seem like a minor detail, the discontinuity can complicate analysis and make it difficult to accurately compare performance across different scenarios. Perhaps we could explore alternative approaches, such as using a default value: If LCP is not captured, we could set TTI to a default value (e.g., the maximum possible value) to indicate that the metric is incomplete or even introducing a new metric: We might consider creating a separate metric that specifically measures time to first interaction without relying on LCP.

Copy link
Member

Choose a reason for hiding this comment

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

So alternatively, we can throw if there's no LCP

and ignore this FCP fallback


/** @type {function(TimePeriod):boolean} */
const isLongEnoughQuietPeriod = period =>
period.end > FcpTsInMs + REQUIRED_QUIET_WINDOW &&
period.end > minTime + REQUIRED_QUIET_WINDOW &&
period.end - period.start >= REQUIRED_QUIET_WINDOW;
const networkQuietPeriods = this._findNetworkQuietPeriods(networkRecords, processedNavigation)
.filter(isLongEnoughQuietPeriod);
Expand Down
26 changes: 20 additions & 6 deletions core/test/computed/metrics/interactive-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ describe('Metrics: TTI', () => {
describe('#findOverlappingQuietPeriods', () => {
it('should return entire range when no activity is present', () => {
const timeOrigin = 220023532;
const firstContentfulPaint = 2500 * 1000 + timeOrigin;
const largestContentfulPaint = 2500 * 1000 + timeOrigin;
const traceEnd = 10000 * 1000 + timeOrigin;
const processedTrace = /** @type {LH.Artifacts.ProcessedNavigation} */ (
{timestamps: {timeOrigin, firstContentfulPaint, traceEnd}}
{timestamps: {timeOrigin, largestContentfulPaint, traceEnd}}
);
const network = generateNetworkRecords([], timeOrigin);

Expand All @@ -105,7 +105,21 @@ describe('Metrics: TTI', () => {
assert.deepEqual(result.networkQuietPeriod, {start: 0, end: traceEnd / 1000});
});

it('should throw when trace ended too soon after FCP', () => {
it('should throw when trace ended too soon after LCP', () => {
const timeOrigin = 220023532;
const largestContentfulPaint = 2500 * 1000 + timeOrigin;
const traceEnd = 5000 * 1000 + timeOrigin;
const processedTrace = /** @type {LH.Artifacts.ProcessedNavigation} */ (
{timestamps: {timeOrigin, largestContentfulPaint, traceEnd}}
);
const network = generateNetworkRecords([], timeOrigin);

assert.throws(() => {
Interactive.findOverlappingQuietPeriods([], network, processedTrace);
}, /NO.*IDLE_PERIOD/);
});

it('should throw when trace ended too soon after FCP (when LCP is missing)', () => {
const timeOrigin = 220023532;
const firstContentfulPaint = 2500 * 1000 + timeOrigin;
const traceEnd = 5000 * 1000 + timeOrigin;
Expand Down Expand Up @@ -183,14 +197,14 @@ describe('Metrics: TTI', () => {

it('should find first overlapping quiet period', () => {
const timeOrigin = 220023532;
const firstContentfulPaint = 10000 * 1000 + timeOrigin;
const largestContentfulPaint = 10000 * 1000 + timeOrigin;
const traceEnd = 45000 * 1000 + timeOrigin;
const processedTrace = /** @type {LH.Artifacts.ProcessedNavigation} */ (
{timestamps: {timeOrigin, firstContentfulPaint, traceEnd}}
{timestamps: {timeOrigin, largestContentfulPaint, traceEnd}}
);

const cpu = [
// quiet period before FCP
// quiet period before LCP
{start: 9000, end: 9900},
{start: 11000, end: 13000},
// quiet period during network activity
Expand Down
Loading