Skip to content

Commit

Permalink
Clean up total blocking time code
Browse files Browse the repository at this point in the history
  • Loading branch information
deepanjanroy committed Jul 18, 2019
1 parent f968ee9 commit 4dfae25
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 151 deletions.
4 changes: 2 additions & 2 deletions lighthouse-cli/test/cli/__snapshots__/index-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Object {
"path": "metrics/estimated-input-latency",
},
Object {
"path": "metrics/cumulative-long-queuing-delay",
"path": "metrics/total-blocking-time",
},
Object {
"path": "metrics/max-potential-fid",
Expand Down Expand Up @@ -717,7 +717,7 @@ Object {
"weight": 0,
},
Object {
"id": "cumulative-long-queuing-delay",
"id": "total-blocking-time",
"weight": 0,
},
Object {
Expand Down
8 changes: 4 additions & 4 deletions lighthouse-core/audits/metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const FirstCPUIdle = require('../computed/metrics/first-cpu-idle.js');
const Interactive = require('../computed/metrics/interactive.js');
const SpeedIndex = require('../computed/metrics/speed-index.js');
const EstimatedInputLatency = require('../computed/metrics/estimated-input-latency.js');
const CumulativeLongQueuingDelay = require('../computed/metrics/cumulative-long-queuing-delay.js');
const TotalBlockingTime = require('../computed/metrics/total-blocking-time.js');

class Metrics extends Audit {
/**
Expand Down Expand Up @@ -60,7 +60,7 @@ class Metrics extends Audit {
const interactive = await requestOrUndefined(Interactive, metricComputationData);
const speedIndex = await requestOrUndefined(SpeedIndex, metricComputationData);
const estimatedInputLatency = await EstimatedInputLatency.request(metricComputationData, context); // eslint-disable-line max-len
const cumulativeLongQueuingDelay = await CumulativeLongQueuingDelay.request(metricComputationData, context); // eslint-disable-line max-len
const totalBlockingTime = await TotalBlockingTime.request(metricComputationData, context); // eslint-disable-line max-len

/** @type {UberMetricsItem} */
const metrics = {
Expand All @@ -77,7 +77,7 @@ class Metrics extends Audit {
speedIndexTs: speedIndex && speedIndex.timestamp,
estimatedInputLatency: estimatedInputLatency.timing,
estimatedInputLatencyTs: estimatedInputLatency.timestamp,
cumulativeLongQueuingDelay: cumulativeLongQueuingDelay.timing,
totalBlockingTime: totalBlockingTime.timing,

// Include all timestamps of interest from trace of tab
observedNavigationStart: traceOfTab.timings.navigationStart,
Expand Down Expand Up @@ -140,7 +140,7 @@ class Metrics extends Audit {
* @property {number=} speedIndexTs
* @property {number} estimatedInputLatency
* @property {number=} estimatedInputLatencyTs
* @property {number} cumulativeLongQueuingDelay
* @property {number} totalBlockingTime
* @property {number} observedNavigationStart
* @property {number} observedNavigationStartTs
* @property {number=} observedFirstPaint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,28 @@
'use strict';

const Audit = require('../audit.js');
const CumulativeLQD = require('../../computed/metrics/cumulative-long-queuing-delay.js');
const ComputedTBT = require('../../computed/metrics/total-blocking-time.js');
const i18n = require('../../lib/i18n/i18n.js');

// TODO(deepanjanroy): i18n strings once metric is final.
const UIStringsNotExported = {
title: 'Cumulative Long Queuing Delay',
description: '[Experimental metric] Total time period between FCP and Time to Interactive ' +
'during which queuing time for any input event would be higher than 50ms.',
const UIStrings = {
/** The name of the metric that calculates the total duration of blocking time for a page. Blocking times are time periods when the page would be slow at responding to user input (clicks, taps, and keypresses will feel janky). Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit. */
title: 'Total Blocking Time',
/** Description of the Total Blocking Time (TBT) metric, which calculates the total duration of blocking time for a page. Blocking times are times when if a page received any input (clicks, taps, and keypresses), the page would be slow at responding. This is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits.*/
description: 'Sum of all time periods between FCP and Time to Interactive, ' +
'when task length exceeded by more than 50ms, expressed in milliseconds.',
};

class CumulativeLongQueuingDelay extends Audit {
const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);

class TotalBlockingTime extends Audit {
/**
* @return {LH.Audit.Meta}
*/
static get meta() {
return {
id: 'cumulative-long-queuing-delay',
title: UIStringsNotExported.title,
description: UIStringsNotExported.description,
id: 'total-blocking-time',
title: str_(UIStrings.title),
description: str_(UIStrings.description),
scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
requiredArtifacts: ['traces', 'devtoolsLogs'],
};
Expand All @@ -46,13 +50,12 @@ class CumulativeLongQueuingDelay extends Audit {
}

/**
* Audits the page to calculate Cumulative Long Queuing Delay.
* Audits the page to calculate Total Blocking Time.
*
* We define Long Queuing Delay Region as any time interval in the loading timeline where queuing
* time for an input event would be longer than 50ms. For example, if there is a 110ms main thread
* task, the first 60ms of it is Long Queuing Delay Region, because any input event occuring in
* that region has to wait more than 50ms. Cumulative Long Queuing Delay is the sum of all Long
* Queuing Delay Regions between First Contentful Paint and Interactive Time (TTI).
* We define Blocking Time as any time interval in the loading timeline where task length exceeds
* 50ms. For example, if there is a 110ms main thread task, the last 60ms of it is blocking time.
* Total Blocking Time is the sum of all Blocking Time between First Contentful Paint and
* Interactive Time (TTI).
*
* @param {LH.Artifacts} artifacts
* @param {LH.Audit.Context} context
Expand All @@ -62,7 +65,7 @@ class CumulativeLongQueuingDelay extends Audit {
const trace = artifacts.traces[Audit.DEFAULT_PASS];
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
const metricComputationData = {trace, devtoolsLog, settings: context.settings};
const metricResult = await CumulativeLQD.request(metricComputationData, context);
const metricResult = await ComputedTBT.request(metricComputationData, context);

return {
score: Audit.computeLogNormalScore(
Expand All @@ -71,9 +74,10 @@ class CumulativeLongQueuingDelay extends Audit {
context.options.scoreMedian
),
numericValue: metricResult.timing,
displayValue: 10 * Math.round(metricResult.timing / 10) + '\xa0ms',
displayValue: str_(i18n.UIStrings.ms, {timeInMs: metricResult.timing}),
};
}
}

module.exports = CumulativeLongQueuingDelay;
module.exports = TotalBlockingTime;
module.exports.UIStrings = UIStrings;
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const LanternInteractive = require('./lantern-interactive.js');

/** @typedef {BaseNode.Node} Node */

class LanternCumulativeLongQueuingDelay extends LanternMetric {
class LanternTotalBlockingTime extends LanternMetric {
/**
* @return {LH.Gatherer.Simulation.MetricCoefficients}
*/
Expand Down Expand Up @@ -48,32 +48,32 @@ class LanternCumulativeLongQueuingDelay extends LanternMetric {
*/
static getEstimateFromSimulation(simulation, extras) {
// Intentionally use the opposite FCP estimate. A pessimistic FCP is higher than equal to an
// optimistic FCP, which means potentially more tasks are excluded from the
// CumulativeLongQueuingDelay computation. So a more pessimistic FCP gives a more optimistic
// CumulativeLongQueuingDelay for the same work.
// optimistic FCP, which means potentially more tasks are excluded from the Total Blocking Time
// computation. So a more pessimistic FCP gives a more optimistic Total Blocking Time for the
// same work.
const fcpTimeInMs = extras.optimistic
? extras.fcpResult.pessimisticEstimate.timeInMs
: extras.fcpResult.optimisticEstimate.timeInMs;

// Similarly, we always have pessimistic TTI >= optimistic TTI. Therefore, picking optimistic
// TTI means our window of interest is smaller and thus potentially more tasks are excluded from
// CumulativeLongQueuingDelay computation, yielding a lower (more optimistic)
// CumulativeLongQueuingDelay value for the same work.
// Total Blocking Time computation, yielding a lower (more optimistic) Total Blocking Time value
// for the same work.
const interactiveTimeMs = extras.optimistic
? extras.interactiveResult.optimisticEstimate.timeInMs
: extras.interactiveResult.pessimisticEstimate.timeInMs;

// Require here to resolve circular dependency.
const CumulativeLongQueuingDelay = require('./cumulative-long-queuing-delay.js');
const minDurationMs = CumulativeLongQueuingDelay.LONG_QUEUING_DELAY_THRESHOLD;
const TotalBlockingTime = require('./total-blocking-time.js');
const minDurationMs = TotalBlockingTime.BLOCKING_TIME_THRESHOLD;

const events = LanternCumulativeLongQueuingDelay.getTopLevelEvents(
const events = LanternTotalBlockingTime.getTopLevelEvents(
simulation.nodeTimings,
minDurationMs
);

return {
timeInMs: CumulativeLongQueuingDelay.calculateSumOfLongQueuingDelay(
timeInMs: TotalBlockingTime.calculateSumOfBlockingTime(
events,
fcpTimeInMs,
interactiveTimeMs
Expand Down Expand Up @@ -118,4 +118,4 @@ class LanternCumulativeLongQueuingDelay extends LanternMetric {
}
}

module.exports = makeComputedArtifact(LanternCumulativeLongQueuingDelay);
module.exports = makeComputedArtifact(LanternTotalBlockingTime);
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,27 @@ const makeComputedArtifact = require('../computed-artifact.js');
const ComputedMetric = require('./metric.js');
const LHError = require('../../lib/lh-error.js');
const TracingProcessor = require('../../lib/tracehouse/trace-processor.js');
const LanternCumulativeLongQueuingDelay = require('./lantern-cumulative-long-queuing-delay.js');
const LanternTotalBlockingTime = require('./lantern-total-blocking-time.js');
const TimetoInteractive = require('./interactive.js');

/**
* @fileoverview This audit determines Cumulative Long Queuing Delay between FCP and TTI.
* @fileoverview This audit determines Total Blocking Time.
* We define Long Queuing Delay Region as any time interval in the loading timeline where queuing
* time for an input event would be longer than 50ms. For example, if there is a 110ms main thread
* task, the first 60ms of it is Long Queuing Delay Region, because any input event occuring in
* that region has to wait more than 50ms. Cumulative Long Queuing Delay is the sum of all Long
* Queuing Delay Regions between First Contentful Paint and Interactive Time (TTI).
* We define Blocking Time as any time interval in the loading timeline where task length exceeds
* 50ms. For example, if there is a 110ms main thread task, the last 60ms of it is blocking time.
* Total Blocking Time is the sum of all Blocking Time between First Contentful Paint and
* Interactive Time (TTI).
*
* This is a new metric designed to accompany Time to Interactive. TTI is strict and does not
* reflect incremental improvements to the site performance unless the improvement concerns the last
* long task. Cumulative Long Queuing Delay on the other hand is designed to be much more responsive
* long task. Total Blocking Time on the other hand is designed to be much more responsive
* to smaller improvements to main thread responsiveness.
*/
class CumulativeLongQueuingDelay extends ComputedMetric {
class TotalBlockingTime extends ComputedMetric {
/**
* @return {number}
*/
static get LONG_QUEUING_DELAY_THRESHOLD() {
static get BLOCKING_TIME_THRESHOLD() {
return 50;
}
/**
Expand All @@ -39,30 +38,29 @@ class CumulativeLongQueuingDelay extends ComputedMetric {
* @param {number} interactiveTimeMs
* @return {number}
*/
static calculateSumOfLongQueuingDelay(topLevelEvents, fcpTimeInMs, interactiveTimeMs) {
static calculateSumOfBlockingTime(topLevelEvents, fcpTimeInMs, interactiveTimeMs) {
if (interactiveTimeMs <= fcpTimeInMs) return 0;

const threshold = CumulativeLongQueuingDelay.LONG_QUEUING_DELAY_THRESHOLD;
const longQueuingDelayRegions = [];
// First identifying the long queuing delay regions.
const threshold = TotalBlockingTime.BLOCKING_TIME_THRESHOLD;
const blockingRegions = [];
for (const event of topLevelEvents) {
// If the task is less than the delay threshold, it contains no Long Queuing Delay Region.
// If the task is less than the delay threshold, it contains no Blocking Region.
if (event.duration < threshold) continue;
// Otherwise, the duration of the task before the delay-threshold-sized interval at the end is
// considered Long Queuing Delay Region. Example assuming the threshold is 50ms:
// Otherwise, the duration of the task beyond blocking time threshold at the beginning is
// considered Blocking Region. Example assuming the threshold is 50ms:
// [ 250ms Task ]
// | Long Queuing Delay Region | Last 50ms |
// 200 ms
longQueuingDelayRegions.push({
start: event.start,
end: event.end - threshold,
// | First 50ms | Blocking Time Region |
// 200 ms
blockingRegions.push({
start: event.start + threshold,
end: event.end,
duration: event.duration - threshold,
});
}

let sumLongQueuingDelay = 0;
for (const region of longQueuingDelayRegions) {
// We only want to add up the Long Queuing Delay regions that fall between FCP and TTI.
let sumBlockingTime = 0;
for (const region of blockingRegions) {
// We only want to add up the Blocking Time Regions that fall between FCP and TTI.
//
// FCP is picked as the lower bound because there is little risk of user input happening
// before FCP so Long Queuing Qelay regions do not harm user experience. Developers should be
Expand All @@ -73,16 +71,16 @@ class CumulativeLongQueuingDelay extends ComputedMetric {
if (region.end < fcpTimeInMs) continue;
if (region.start > interactiveTimeMs) continue;

// If a Long Queuing Delay Region spans the edges of our region of interest, we clip it to
// only include the part of the region that falls inside.
// If a Blocking Time Region spans the edges of our region of interest, we clip it to only
// include the part of the region that falls inside.
const clippedStart = Math.max(region.start, fcpTimeInMs);
const clippedEnd = Math.min(region.end, interactiveTimeMs);
const queuingDelayAfterClipping = clippedEnd - clippedStart;
const blockingTimeAfterClipping = clippedEnd - clippedStart;

sumLongQueuingDelay += queuingDelayAfterClipping;
sumBlockingTime += blockingTimeAfterClipping;
}

return sumLongQueuingDelay;
return sumBlockingTime;
}

/**
Expand All @@ -91,7 +89,7 @@ class CumulativeLongQueuingDelay extends ComputedMetric {
* @return {Promise<LH.Artifacts.LanternMetric>}
*/
static computeSimulatedMetric(data, context) {
return LanternCumulativeLongQueuingDelay.request(data, context);
return LanternTotalBlockingTime.request(data, context);
}

/**
Expand All @@ -112,7 +110,7 @@ class CumulativeLongQueuingDelay extends ComputedMetric {
const events = TracingProcessor.getMainThreadTopLevelEvents(data.traceOfTab);

return {
timing: CumulativeLongQueuingDelay.calculateSumOfLongQueuingDelay(
timing: TotalBlockingTime.calculateSumOfBlockingTime(
events,
firstContentfulPaint,
interactiveTimeMs
Expand All @@ -121,4 +119,4 @@ class CumulativeLongQueuingDelay extends ComputedMetric {
}
}

module.exports = makeComputedArtifact(CumulativeLongQueuingDelay);
module.exports = makeComputedArtifact(TotalBlockingTime);
4 changes: 2 additions & 2 deletions lighthouse-core/config/default-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ const defaultConfig = {
'screenshot-thumbnails',
'final-screenshot',
'metrics/estimated-input-latency',
'metrics/cumulative-long-queuing-delay',
'metrics/total-blocking-time',
'metrics/max-potential-fid',
'errors-in-console',
'time-to-first-byte',
Expand Down Expand Up @@ -369,7 +369,7 @@ const defaultConfig = {
{id: 'first-cpu-idle', weight: 2, group: 'metrics'},
{id: 'max-potential-fid', weight: 0, group: 'metrics'},
{id: 'estimated-input-latency', weight: 0}, // intentionally left out of metrics so it won't be displayed
{id: 'cumulative-long-queuing-delay', weight: 0}, // intentionally left out of metrics so it won't be displayed
{id: 'total-blocking-time', weight: 0}, // intentionally left out of metrics so it won't be displayed
{id: 'render-blocking-resources', weight: 0, group: 'load-opportunities'},
{id: 'uses-responsive-images', weight: 0, group: 'load-opportunities'},
{id: 'offscreen-images', weight: 0, group: 'load-opportunities'},
Expand Down
8 changes: 8 additions & 0 deletions lighthouse-core/lib/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,14 @@
"message": "Speed Index",
"description": "The name of the metric that summarizes how quickly the page looked visually complete. The name of this metric is largely abstract and can be loosely translated. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."
},
"lighthouse-core/audits/metrics/total-blocking-time.js | description": {
"message": "Sum of all time periods between FCP and Time to Interactive, when task length exceeded by more than 50ms, expressed in milliseconds.",
"description": "Description of the Total Blocking Time (TBT) metric, which calculates the total duration of blocking time for a page. Blocking times are times when if a page received any input (clicks, taps, and keypresses), the page would be slow at responding. This is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits."
},
"lighthouse-core/audits/metrics/total-blocking-time.js | title": {
"message": "Total Blocking Time",
"description": "The name of the metric that calculates the total duration of blocking time for a page. Blocking times are time periods when the page would be slow at responding to user input (clicks, taps, and keypresses will feel janky). Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."
},
"lighthouse-core/audits/network-rtt.js | description": {
"message": "Network round trip times (RTT) have a large impact on performance. If the RTT to an origin is high, it's an indication that servers closer to the user could improve performance. [Learn more](https://hpbn.co/primer-on-latency-and-bandwidth/).",
"description": "Description of a Lighthouse audit that tells the user that a high network round trip time (RTT) can effect their website's performance because the server is physically far away from them thus making the RTT high. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."
Expand Down
Loading

0 comments on commit 4dfae25

Please sign in to comment.