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: add desktop score curves for TBT and LCP #10756

Merged
merged 2 commits into from
May 13, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
39 changes: 30 additions & 9 deletions lighthouse-core/audits/metrics/largest-contentful-paint.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,39 @@ class LargestContentfulPaint extends Audit {
title: str_(i18n.UIStrings.largestContentfulPaintMetric),
description: str_(UIStrings.description),
scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
requiredArtifacts: ['traces', 'devtoolsLogs'],
requiredArtifacts: ['traces', 'devtoolsLogs', 'TestedAsMobileDevice'],
};
}

/**
* @return {LH.Audit.ScoreOptions}
* @return {{mobile: {scoring: LH.Audit.ScoreOptions}, desktop: {scoring: LH.Audit.ScoreOptions}}}
*/
static get defaultOptions() {
return {
// 25th and 13th percentiles HTTPArchive -> median and p10 points.
// https://bigquery.cloud.google.com/table/httparchive:lighthouse.2020_02_01_mobile?pli=1
// https://web.dev/lcp/#what-is-a-good-lcp-score
// see https://www.desmos.com/calculator/1etesp32kt
p10: 2500,
median: 4000,
mobile: {
// 25th and 13th percentiles HTTPArchive -> median and p10 points.
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is this 13?

is this the x percentile of all web traffic or just desktop? (same question for mobile control points). comment doesn't make it clear, but the link to bigquery specifies mobile so I think it's filtering device type?

Copy link
Member Author

Choose a reason for hiding this comment

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

why is this 13?

#10715 changed the second control point from the PODR to p10, but in order to not change the curves, the p10 point ended up at a somewhat arbitrary percentile for each audit.

(this PR isn't changing any of the mobile numbers, though (?w=1 makes this a little clearer))

is this the x percentile of all web traffic or just desktop? (same question for mobile control points). comment doesn't make it clear, but the link to bigquery specifies mobile so I think it's filtering device type?

Right, the mobile and desktop data come from the percentiles in the respective http archive tables (*_mobile and *_desktop) mentioned in each comment.

Copy link
Collaborator

@patrickhulce patrickhulce May 12, 2020

Choose a reason for hiding this comment

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

I think this was decided to leave the LCP points as they were at some point prior and this is just a noop whitespace change.

Commented at the same time as @brendankenny but he explained better :)

// https://bigquery.cloud.google.com/table/httparchive:lighthouse.2020_02_01_mobile?pli=1
// https://web.dev/lcp/#what-is-a-good-lcp-score
// see https://www.desmos.com/calculator/1etesp32kt
scoring: {
p10: 2500,
median: 4000,
},
},
desktop: {
// 25th and 5th percentiles HTTPArchive -> median and p10 points.
// SELECT
// APPROX_QUANTILES(lcpValue, 100)[OFFSET(5)] AS p05_lcp,
// APPROX_QUANTILES(lcpValue, 100)[OFFSET(25)] AS p25_lcp
// FROM (
// SELECT CAST(JSON_EXTRACT_SCALAR(payload, "$['_chromeUserTiming.LargestContentfulPaint']") AS NUMERIC) AS lcpValue
// FROM `httparchive.pages.2020_04_01_desktop`
// )
scoring: {
p10: 1200,
median: 2400,
},
},
};
}

Expand All @@ -56,9 +74,12 @@ class LargestContentfulPaint extends Audit {
const metricComputationData = {trace, devtoolsLog, settings: context.settings};
const metricResult = await ComputedLcp.request(metricComputationData, context);

const isDesktop = artifacts.TestedAsMobileDevice === false;
const options = isDesktop ? context.options.desktop : context.options.mobile;

return {
score: Audit.computeLogNormalScore(
{p10: context.options.p10, median: context.options.median},
options.scoring,
metricResult.timing
),
numericValue: metricResult.timing,
Expand Down
43 changes: 32 additions & 11 deletions lighthouse-core/audits/metrics/total-blocking-time.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,41 @@ class TotalBlockingTime extends Audit {
title: str_(i18n.UIStrings.totalBlockingTimeMetric),
description: str_(UIStrings.description),
scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
requiredArtifacts: ['traces', 'devtoolsLogs'],
requiredArtifacts: ['traces', 'devtoolsLogs', 'TestedAsMobileDevice'],
};
}

/**
* @return {LH.Audit.ScoreOptions}
* @return {{mobile: {scoring: LH.Audit.ScoreOptions}, desktop: {scoring: LH.Audit.ScoreOptions}}}
*/
static get defaultOptions() {
return {
// According to a cluster telemetry run over top 10k sites on mobile, 5th percentile was 0ms,
// 25th percentile was 270ms and median was 895ms. These numbers include 404 pages. Picking
// thresholds according to our 25/75-th rule will be quite harsh scoring (a single 350ms task)
// after FCP will yield a score of .5. The following coefficients are semi-arbitrarily picked
// to give 600ms jank a score of .5 and 100ms jank a score of .999. We can tweak these numbers
// in the future. See https://www.desmos.com/calculator/bbsv8fedg5
median: 600,
p10: 287,
mobile: {
// According to a cluster telemetry run over top 10k sites on mobile, 5th percentile was 0ms,
// 25th percentile was 270ms and median was 895ms. These numbers include 404 pages. Picking
// thresholds according to our 25/75-th rule will be quite harsh scoring (a single 350ms task)
// after FCP will yield a score of .5. The following coefficients are semi-arbitrarily picked
// to give 600ms jank a score of .5 and 100ms jank a score of .999. We can tweak these numbers
// in the future. See https://www.desmos.com/calculator/bbsv8fedg5
scoring: {
p10: 287,
median: 600,
},
},
desktop: {
// Chosen in HTTP Archive desktop results to approximate curve easing described above.
// SELECT
// APPROX_QUANTILES(tbtValue, 100)[OFFSET(40)] AS p40_tbt,
// APPROX_QUANTILES(tbtValue, 100)[OFFSET(60)] AS p60_tbt
// FROM (
// SELECT CAST(JSON_EXTRACT_SCALAR(payload, '$._TotalBlockingTime') AS NUMERIC) AS tbtValue
// FROM `httparchive.pages.2020_04_01_desktop`
// )
scoring: {
p10: 150,
median: 350,
},
},
};
}

Expand All @@ -65,9 +83,12 @@ class TotalBlockingTime extends Audit {
const metricComputationData = {trace, devtoolsLog, settings: context.settings};
const metricResult = await ComputedTBT.request(metricComputationData, context);

const isDesktop = artifacts.TestedAsMobileDevice === false;
const options = isDesktop ? context.options.desktop : context.options.mobile;

return {
score: Audit.computeLogNormalScore(
{p10: context.options.p10, median: context.options.median},
options.scoring,
metricResult.timing
),
numericValue: metricResult.timing,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* @license Copyright 2020 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

const LCPAudit = require('../../../audits/metrics/largest-contentful-paint.js');
const defaultOptions = LCPAudit.defaultOptions;

const trace = require('../../fixtures/traces/lcp-m78.json');
const devtoolsLog = require('../../fixtures/traces/lcp-m78.devtools.log.json');

function generateArtifacts({trace, devtoolsLog, TestedAsMobileDevice}) {
return {
traces: {[LCPAudit.DEFAULT_PASS]: trace},
devtoolsLogs: {[LCPAudit.DEFAULT_PASS]: devtoolsLog},
TestedAsMobileDevice,
};
}

function generateContext({throttlingMethod}) {
const settings = {throttlingMethod};
return {options: defaultOptions, settings, computedCache: new Map()};
}
/* eslint-env jest */

describe('Performance: largest-contentful-paint audit', () => {
it('adjusts scoring based on form factor', async () => {
const artifactsMobile = generateArtifacts({trace, devtoolsLog, TestedAsMobileDevice: true});
const contextMobile = generateContext({throttlingMethod: 'provided'});

const outputMobile = await LCPAudit.audit(artifactsMobile, contextMobile);
expect(outputMobile.numericValue).toBeCloseTo(1121.711, 1);
expect(outputMobile.score).toBe(1);
expect(outputMobile.displayValue).toBeDisplayString('1.1\xa0s');

const artifactsDesktop = generateArtifacts({trace, devtoolsLog, TestedAsMobileDevice: false});
const contextDesktop = generateContext({throttlingMethod: 'provided'});

const outputDesktop = await LCPAudit.audit(artifactsDesktop, contextDesktop);
expect(outputDesktop.numericValue).toBeCloseTo(1121.711, 1);
expect(outputDesktop.score).toBe(0.92);
expect(outputDesktop.displayValue).toBeDisplayString('1.1\xa0s');
});
});
45 changes: 37 additions & 8 deletions lighthouse-core/test/audits/metrics/total-blocking-time-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,56 @@
'use strict';

const TBTAudit = require('../../../audits/metrics/total-blocking-time.js');
const options = TBTAudit.defaultOptions;
const defaultOptions = TBTAudit.defaultOptions;

const pwaTrace = require('../../fixtures/traces/progressive-app-m60.json');
const trace = require('../../fixtures/traces/progressive-app-m60.json');
const devtoolsLog = require('../../fixtures/traces/progressive-app-m60.devtools.log.json');

function generateArtifactsWithTrace(trace) {
const lcpTrace = require('../../fixtures/traces/lcp-m78.json');
const lcpDevtoolsLog = require('../../fixtures/traces/lcp-m78.devtools.log.json');

function generateArtifacts({trace, devtoolsLog, TestedAsMobileDevice}) {
return {
traces: {[TBTAudit.DEFAULT_PASS]: trace},
devtoolsLogs: {[TBTAudit.DEFAULT_PASS]: []},
devtoolsLogs: {[TBTAudit.DEFAULT_PASS]: devtoolsLog},
TestedAsMobileDevice,
};
}

function generateContext({throttlingMethod}) {
const settings = {throttlingMethod};
return {options: defaultOptions, settings, computedCache: new Map()};
}
/* eslint-env jest */

describe('Performance: total-blocking-time audit', () => {
it('evaluates Total Blocking Time metric properly', async () => {
const artifacts = generateArtifactsWithTrace(pwaTrace);
const settings = {throttlingMethod: 'provided'};
const context = {options, settings, computedCache: new Map()};
const output = await TBTAudit.audit(artifacts, context);
const artifacts = generateArtifacts({trace, devtoolsLog, TestedAsMobileDevice: true});
const context = generateContext({throttlingMethod: 'provided'});

const output = await TBTAudit.audit(artifacts, context);
expect(output.numericValue).toBeCloseTo(48.3, 1);
expect(output.score).toBe(1);
expect(output.displayValue).toBeDisplayString('50\xa0ms');
});

it('adjusts scoring based on form factor', async () => {
const artifactsMobile = generateArtifacts({trace: lcpTrace,
devtoolsLog: lcpDevtoolsLog, TestedAsMobileDevice: true});
const contextMobile = generateContext({throttlingMethod: 'provided'});

const outputMobile = await TBTAudit.audit(artifactsMobile, contextMobile);
expect(outputMobile.numericValue).toBeCloseTo(333, 1);
expect(outputMobile.score).toBe(0.85);
expect(outputMobile.displayValue).toBeDisplayString('330\xa0ms');

const artifactsDesktop = generateArtifacts({trace: lcpTrace,
devtoolsLog: lcpDevtoolsLog, TestedAsMobileDevice: false});
const contextDesktop = generateContext({throttlingMethod: 'provided'});

const outputDesktop = await TBTAudit.audit(artifactsDesktop, contextDesktop);
expect(outputDesktop.numericValue).toBeCloseTo(333, 1);
expect(outputDesktop.score).toBe(0.53);
expect(outputDesktop.displayValue).toBeDisplayString('330\xa0ms');
});
});