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(metrics): move TTCI to computed artifact #4943

Merged
merged 6 commits into from
Apr 10, 2018
Merged
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
190 changes: 22 additions & 168 deletions lighthouse-core/audits/consistently-interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@

const Audit = require('./audit');
const Util = require('../report/v2/renderer/util');
const NetworkRecorder = require('../lib/network-recorder');
const TracingProcessor = require('../lib/traces/tracing-processor');
const LHError = require('../lib/errors');

const REQUIRED_QUIET_WINDOW = 5000;
const ALLOWED_CONCURRENT_REQUESTS = 2;

/**
* @fileoverview This audit identifies the time the page is "consistently interactive".
Expand Down Expand Up @@ -47,177 +41,37 @@ class ConsistentlyInteractiveMetric extends Audit {
};
}

/**
* Finds all time periods where the number of inflight requests is less than or equal to the
* number of allowed concurrent requests (2).
* @param {Array<LH.WebInspector.NetworkRequest>} networkRecords
* @param {{timestamps: {traceEnd: number}}} traceOfTab
* @return {!Array<!TimePeriod>}
*/
static _findNetworkQuietPeriods(networkRecords, traceOfTab) {
const traceEndTsInMs = traceOfTab.timestamps.traceEnd / 1000;
return NetworkRecorder.findNetworkQuietPeriods(networkRecords,
ALLOWED_CONCURRENT_REQUESTS, traceEndTsInMs);
}

/**
* Finds all time periods where there are no long tasks.
* @param {!Array<!TimePeriod>} longTasks
* @param {{timestamps: {navigationStart: number, traceEnd: number}}} traceOfTab
* @return {!Array<!TimePeriod>}
*/
static _findCPUQuietPeriods(longTasks, traceOfTab) {
const navStartTsInMs = traceOfTab.timestamps.navigationStart / 1000;
const traceEndTsInMs = traceOfTab.timestamps.traceEnd / 1000;
if (longTasks.length === 0) {
return [{start: 0, end: traceEndTsInMs}];
}

const quietPeriods = [];
longTasks.forEach((task, index) => {
if (index === 0) {
quietPeriods.push({
start: 0,
end: task.start + navStartTsInMs,
});
}

if (index === longTasks.length - 1) {
quietPeriods.push({
start: task.end + navStartTsInMs,
end: traceEndTsInMs,
});
} else {
quietPeriods.push({
start: task.end + navStartTsInMs,
end: longTasks[index + 1].start + navStartTsInMs,
});
}
});

return quietPeriods;
}

/**
* Finds the first time period where a network quiet period and a CPU quiet period overlap.
* @param {!Array<!TimePeriod>} longTasks
* @param {Array<LH.WebInspector.NetworkRequest>} networkRecords
* @param {{timestamps: {navigationStart: number, firstMeaningfulPaint: number,
* traceEnd: number}}} traceOfTab
* @return {{cpuQuietPeriod: !TimePeriod, networkQuietPeriod: !TimePeriod,
* cpuQuietPeriods: !Array<!TimePeriod>, networkQuietPeriods: !Array<!TimePeriod>}}
*/
static findOverlappingQuietPeriods(longTasks, networkRecords, traceOfTab) {
const FMPTsInMs = traceOfTab.timestamps.firstMeaningfulPaint / 1000;

const isLongEnoughQuietPeriod = period =>
period.end > FMPTsInMs + REQUIRED_QUIET_WINDOW &&
period.end - period.start >= REQUIRED_QUIET_WINDOW;
const networkQuietPeriods = this._findNetworkQuietPeriods(networkRecords, traceOfTab)
.filter(isLongEnoughQuietPeriod);
const cpuQuietPeriods = this._findCPUQuietPeriods(longTasks, traceOfTab)
.filter(isLongEnoughQuietPeriod);

const cpuQueue = cpuQuietPeriods.slice();
const networkQueue = networkQuietPeriods.slice();

// We will check for a CPU quiet period contained within a Network quiet period or vice-versa
let cpuCandidate = cpuQueue.shift();
let networkCandidate = networkQueue.shift();
while (cpuCandidate && networkCandidate) {
if (cpuCandidate.start >= networkCandidate.start) {
// CPU starts later than network, window must be contained by network or we check the next
if (networkCandidate.end >= cpuCandidate.start + REQUIRED_QUIET_WINDOW) {
return {
cpuQuietPeriod: cpuCandidate,
networkQuietPeriod: networkCandidate,
cpuQuietPeriods,
networkQuietPeriods,
};
} else {
networkCandidate = networkQueue.shift();
}
} else {
// Network starts later than CPU, window must be contained by CPU or we check the next
if (cpuCandidate.end >= networkCandidate.start + REQUIRED_QUIET_WINDOW) {
return {
cpuQuietPeriod: cpuCandidate,
networkQuietPeriod: networkCandidate,
cpuQuietPeriods,
networkQuietPeriods,
};
} else {
cpuCandidate = cpuQueue.shift();
}
}
}

throw new LHError(
cpuCandidate
? LHError.errors.NO_TTI_NETWORK_IDLE_PERIOD
: LHError.errors.NO_TTI_CPU_IDLE_PERIOD
);
}

/**
* @param {!Artifacts} artifacts
* @param {LH.Audit.Context} context
* @return {!Promise<!AuditResult>}
*/
static audit(artifacts, context) {
static async audit(artifacts, context) {
const trace = artifacts.traces[Audit.DEFAULT_PASS];
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
const computedArtifacts = [
artifacts.requestNetworkRecords(devtoolsLog),
artifacts.requestTraceOfTab(trace),
];

return Promise.all(computedArtifacts)
.then(([networkRecords, traceOfTab]) => {
if (!traceOfTab.timestamps.firstMeaningfulPaint) {
throw new LHError(LHError.errors.NO_FMP);
}

if (!traceOfTab.timestamps.domContentLoaded) {
throw new LHError(LHError.errors.NO_DCL);
}

const longTasks = TracingProcessor.getMainThreadTopLevelEvents(traceOfTab)
.filter(event => event.duration >= 50);
const quietPeriodInfo = this.findOverlappingQuietPeriods(longTasks, networkRecords,
traceOfTab);
const cpuQuietPeriod = quietPeriodInfo.cpuQuietPeriod;

const timestamp = Math.max(
cpuQuietPeriod.start,
traceOfTab.timestamps.firstMeaningfulPaint / 1000,
traceOfTab.timestamps.domContentLoaded / 1000
) * 1000;
const timeInMs = (timestamp - traceOfTab.timestamps.navigationStart) / 1000;
const extendedInfo = Object.assign(quietPeriodInfo, {timestamp, timeInMs});
Copy link
Member

Choose a reason for hiding this comment

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

just to call out, this PR does remove quietPeriodInfo from the extInfo, but i think that's just fine. ;)

const metricComputationData = {trace, devtoolsLog, settings: context.settings};
const metricResult = await artifacts.requestConsistentlyInteractive(metricComputationData);
const timeInMs = metricResult.timing;
const extendedInfo = {
timeInMs,
timestamp: metricResult.timestamp,
optimistic: metricResult.optimisticEstimate && metricResult.optimisticEstimate.timeInMs,
pessimistic: metricResult.pessimisticEstimate && metricResult.pessimisticEstimate.timeInMs,
};

return {
score: Audit.computeLogNormalScore(
timeInMs,
context.options.scorePODR,
context.options.scoreMedian
),
rawValue: timeInMs,
displayValue: Util.formatMilliseconds(timeInMs),
extendedInfo: {
value: extendedInfo,
},
};
});
return {
score: Audit.computeLogNormalScore(
timeInMs,
context.options.scorePODR,
context.options.scoreMedian
),
rawValue: timeInMs,
displayValue: Util.formatMilliseconds(timeInMs),
extendedInfo: {
value: extendedInfo,
},
};
}
}

module.exports = ConsistentlyInteractiveMetric;

/**
* @typedef {{
* start: number,
* end: number,
* }}
*/
let TimePeriod; // eslint-disable-line no-unused-vars
2 changes: 1 addition & 1 deletion lighthouse-core/closure/typedefs/ComputedArtifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ ComputedArtifacts.prototype.requestNetworkThroughput;
/** @type {function(!Trace): !Promise<!SpeedlineArtifact>} */
ComputedArtifacts.prototype.requestSpeedline;

/** @type {function(!Trace): !Promise<!TraceOfTabArtifact>} */
/** @type {function(!Trace): !Promise<LH.Gatherer.Artifact.TraceOfTab>} */
ComputedArtifacts.prototype.requestTraceOfTab;

/** @type {function(!Trace): !Promise<{timeInMs: number, timestamp: number}>} */
Expand Down
2 changes: 1 addition & 1 deletion lighthouse-core/gather/computed/first-interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class FirstInteractive extends ComputedArtifact {
}

/**
* @param {!TraceOfTabArtifact} traceOfTab
* @param {LH.Gatherer.Artifact.TraceOfTab} traceOfTab
* @return {{timeInMs: number, timestamp: number}}
*/
computeWithArtifacts(traceOfTab) {
Expand Down
Loading