From 9836f5f844ec05a6c04fc6dd0542dbb23501ec1c Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 25 Mar 2024 15:29:55 -0700 Subject: [PATCH] MaxPotentialFID --- .../metrics/lantern-max-potential-fid.js | 75 +++------------ core/lib/lantern/metrics/max-potential-fid.js | 92 +++++++++++++++++++ 2 files changed, 103 insertions(+), 64 deletions(-) create mode 100644 core/lib/lantern/metrics/max-potential-fid.js diff --git a/core/computed/metrics/lantern-max-potential-fid.js b/core/computed/metrics/lantern-max-potential-fid.js index 1a3fbfa0e04b..894c4431c652 100644 --- a/core/computed/metrics/lantern-max-potential-fid.js +++ b/core/computed/metrics/lantern-max-potential-fid.js @@ -5,63 +5,21 @@ */ import {makeComputedArtifact} from '../computed-artifact.js'; -import {LanternMetric} from './lantern-metric.js'; -import {BaseNode} from '../../lib/lantern/base-node.js'; +import {MaxPotentialFID} from '../../lib/lantern/metrics/max-potential-fid.js'; +import {getComputationDataParams} from './lantern-metric.js'; import {LanternFirstContentfulPaint} from './lantern-first-contentful-paint.js'; -/** @typedef {import('../../lib/lantern/base-node.js').Node} Node */ +/** @typedef {import('../../lib/lantern/metric.js').Extras} Extras */ -class LanternMaxPotentialFID extends LanternMetric { +class LanternMaxPotentialFID extends MaxPotentialFID { /** - * @return {LH.Gatherer.Simulation.MetricCoefficients} - */ - static get COEFFICIENTS() { - return { - intercept: 0, - optimistic: 0.5, - pessimistic: 0.5, - }; - } - - /** - * @param {Node} dependencyGraph - * @return {Node} - */ - static getOptimisticGraph(dependencyGraph) { - return dependencyGraph; - } - - /** - * @param {Node} dependencyGraph - * @return {Node} - */ - static getPessimisticGraph(dependencyGraph) { - return dependencyGraph; - } - - /** - * @param {LH.Gatherer.Simulation.Result} simulation - * @param {import('../../lib/lantern/metric.js').Extras} extras - * @return {LH.Gatherer.Simulation.Result} + * @param {LH.Artifacts.MetricComputationDataInput} data + * @param {LH.Artifacts.ComputedContext} context + * @param {Omit=} extras + * @return {Promise} */ - static getEstimateFromSimulation(simulation, extras) { - if (!extras.fcpResult) throw new Error('missing fcpResult'); - - // Intentionally use the opposite FCP estimate, a more pessimistic FCP means that more tasks - // are excluded from the FID computation, so a higher FCP means lower FID for same work. - const fcpTimeInMs = extras.optimistic - ? extras.fcpResult.pessimisticEstimate.timeInMs - : extras.fcpResult.optimisticEstimate.timeInMs; - - const timings = LanternMaxPotentialFID.getTimingsAfterFCP( - simulation.nodeTimings, - fcpTimeInMs - ); - - return { - timeInMs: Math.max(...timings.map(timing => timing.duration), 16), - nodeTimings: simulation.nodeTimings, - }; + static async computeMetricWithGraphs(data, context, extras) { + return this.compute(await getComputationDataParams(data, context), extras); } /** @@ -71,18 +29,7 @@ class LanternMaxPotentialFID extends LanternMetric { */ static async compute_(data, context) { const fcpResult = await LanternFirstContentfulPaint.request(data, context); - return super.computeMetricWithGraphs(data, context, {fcpResult}); - } - - /** - * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings - * @param {number} fcpTimeInMs - * @return {Array<{duration: number}>} - */ - static getTimingsAfterFCP(nodeTimings, fcpTimeInMs) { - return Array.from(nodeTimings.entries()) - .filter(([node, timing]) => node.type === BaseNode.TYPES.CPU && timing.endTime > fcpTimeInMs) - .map(([_, timing]) => timing); + return this.computeMetricWithGraphs(data, context, {fcpResult}); } } diff --git a/core/lib/lantern/metrics/max-potential-fid.js b/core/lib/lantern/metrics/max-potential-fid.js new file mode 100644 index 000000000000..affd770f42a7 --- /dev/null +++ b/core/lib/lantern/metrics/max-potential-fid.js @@ -0,0 +1,92 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Lantern from '../types/lantern.js'; +import {Metric} from '../metric.js'; +import {BaseNode} from '../base-node.js'; + +/** @typedef {import('../base-node.js').Node} Node */ + +class MaxPotentialFID extends Metric { + /** + * @return {LH.Gatherer.Simulation.MetricCoefficients} + */ + static get COEFFICIENTS() { + return { + intercept: 0, + optimistic: 0.5, + pessimistic: 0.5, + }; + } + + /** + * @param {Node} dependencyGraph + * @return {Node} + */ + static getOptimisticGraph(dependencyGraph) { + return dependencyGraph; + } + + /** + * @param {Node} dependencyGraph + * @return {Node} + */ + static getPessimisticGraph(dependencyGraph) { + return dependencyGraph; + } + + /** + * @param {LH.Gatherer.Simulation.Result} simulation + * @param {import('../metric.js').Extras} extras + * @return {LH.Gatherer.Simulation.Result} + */ + static getEstimateFromSimulation(simulation, extras) { + if (!extras.fcpResult) throw new Error('missing fcpResult'); + + // Intentionally use the opposite FCP estimate, a more pessimistic FCP means that more tasks + // are excluded from the FID computation, so a higher FCP means lower FID for same work. + const fcpTimeInMs = extras.optimistic + ? extras.fcpResult.pessimisticEstimate.timeInMs + : extras.fcpResult.optimisticEstimate.timeInMs; + + const timings = MaxPotentialFID.getTimingsAfterFCP( + simulation.nodeTimings, + fcpTimeInMs + ); + + return { + timeInMs: Math.max(...timings.map(timing => timing.duration), 16), + nodeTimings: simulation.nodeTimings, + }; + } + + /** + * @param {Lantern.Simulation.MetricComputationDataInput} data + * @param {Omit=} extras + * @return {Promise} + */ + static compute(data, extras) { + const fcpResult = extras?.fcpResult; + if (!fcpResult) { + throw new Error('FCP is required to calculate the Max Potential FID metric'); + } + + return super.compute(data, extras); + } + + /** + * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings + * @param {number} fcpTimeInMs + * @return {Array<{duration: number}>} + */ + static getTimingsAfterFCP(nodeTimings, fcpTimeInMs) { + return Array.from(nodeTimings.entries()) + .filter(([node, timing]) => node.type === BaseNode.TYPES.CPU && timing.endTime > fcpTimeInMs) + .map(([_, timing]) => timing); + } +} + +export {MaxPotentialFID};