Skip to content

Commit

Permalink
core(preconnect): use lantern for savings
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickhulce committed Apr 30, 2018
1 parent 756ee63 commit 63698a0
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 18 deletions.
25 changes: 19 additions & 6 deletions lighthouse-core/audits/uses-rel-preconnect.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,23 @@ class UsesRelPreconnectAudit extends Audit {

/**
* @param {LH.Artifacts} artifacts
* @param {LH.Audit.Context} context
* @return {Promise<LH.Audit.Product>}
*/
static async audit(artifacts) {
static async audit(artifacts, context) {
const devtoolsLog = artifacts.devtoolsLogs[UsesRelPreconnectAudit.DEFAULT_PASS];
const URL = artifacts.URL;
const settings = context.settings;
let maxWasted = 0;

const [networkRecords, mainResource] = await Promise.all([
const [networkRecords, mainResource, loadSimulator] = await Promise.all([
artifacts.requestNetworkRecords(devtoolsLog),
artifacts.requestMainResource({devtoolsLog, URL}),
artifacts.requestLoadSimulator({devtoolsLog, settings}),
]);

const {rtt, additionalRttByOrigin} = loadSimulator.getOptions();

/** @type {Map<string, LH.WebInspector.NetworkRequest[]>} */
const origins = new Map();
networkRecords
Expand Down Expand Up @@ -113,17 +118,25 @@ class UsesRelPreconnectAudit extends Audit {
return (record.startTime < firstRecord.startTime) ? record: firstRecord;
});

const connectionTime =
firstRecordOfOrigin._timing.connectEnd - firstRecordOfOrigin._timing.dnsStart;
const securityOrigin = firstRecordOfOrigin.parsedURL.securityOrigin();

// Approximate the connection time with the duration of TCP (+potentially SSL) handshake
// DNS time can be large but can also be 0 if a commonly used origin that's cached, so make
// no assumption about DNS.
const additionalRtt = additionalRttByOrigin.get(securityOrigin) || 0;
let connectionTime = rtt + additionalRtt;
// TCP Handshake will be at least 2 RTTs for TLS connections
if (firstRecordOfOrigin.parsedURL.scheme === 'https') connectionTime = connectionTime * 2;

const timeBetweenMainResourceAndDnsStart =
firstRecordOfOrigin.startTime * 1000 -
mainResource.endTime * 1000 +
firstRecordOfOrigin._timing.dnsStart;

const wastedMs = Math.min(connectionTime, timeBetweenMainResourceAndDnsStart);
maxWasted = Math.max(wastedMs, maxWasted);
results.push({
url: firstRecordOfOrigin.parsedURL.securityOrigin(),
type: 'ms',
url: securityOrigin,
wastedMs: wastedMs,
});
});
Expand Down
10 changes: 10 additions & 0 deletions lighthouse-core/lib/dependency-graph/simulator/simulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,16 @@ class Simulator {
* @param {LH.Gatherer.Simulation.Options} [options]
*/
constructor(options) {
/** @type {Required<LH.Gatherer.Simulation.Options>} */
this._options = Object.assign(
{
rtt: mobile3G.rttMs,
throughput: mobile3G.throughputKbps * 1024,
maximumConcurrentRequests: DEFAULT_MAXIMUM_CONCURRENT_REQUESTS,
cpuSlowdownMultiplier: mobile3G.cpuSlowdownMultiplier,
layoutTaskMultiplier: DEFAULT_LAYOUT_TASK_MULTIPLIER,
additionalRttByOrigin: new Map(),
serverResponseTimeByOrigin: new Map(),
},
options
);
Expand Down Expand Up @@ -278,6 +281,13 @@ class Simulator {
}
}

/**
* @return {Required<LH.Gatherer.Simulation.Options>}
*/
getOptions() {
return this._options;
}

/**
* Estimates the time taken to process all of the graph's nodes.
* @param {Node} graph
Expand Down
49 changes: 38 additions & 11 deletions lighthouse-core/test/audits/uses-rel-preconnect-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ const mainResource = {
};

describe('Performance: uses-rel-preconnect audit', () => {
let simulator;
let simulatorOptions;

beforeEach(() => {
simulator = {getOptions: () => simulatorOptions};
simulatorOptions = {
rtt: 100,
additionalRttByOrigin: new Map(),
};
});

it(`shouldn't suggest preconnect for same origin`, async () => {
const networkRecords = [
mainResource,
Expand All @@ -34,9 +45,10 @@ describe('Performance: uses-rel-preconnect audit', () => {
devtoolsLogs: {[UsesRelPreconnect.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(networkRecords),
requestMainResource: () => Promise.resolve(mainResource),
requestLoadSimulator: () => Promise.resolve(simulator),
};

const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts);
const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts, {});
assert.equal(score, 1);
assert.equal(rawValue, 0);
assert.equal(details.items.length, 0);
Expand All @@ -54,9 +66,10 @@ describe('Performance: uses-rel-preconnect audit', () => {
devtoolsLogs: {[UsesRelPreconnect.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(networkRecords),
requestMainResource: () => Promise.resolve(mainResource),
requestLoadSimulator: () => Promise.resolve(simulator),
};

const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts);
const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts, {});
assert.equal(score, 1);
assert.equal(rawValue, 0);
assert.equal(details.items.length, 0);
Expand All @@ -74,9 +87,10 @@ describe('Performance: uses-rel-preconnect audit', () => {
devtoolsLogs: {[UsesRelPreconnect.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(networkRecords),
requestMainResource: () => Promise.resolve(mainResource),
requestLoadSimulator: () => Promise.resolve(simulator),
};

const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts);
const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts, {});
assert.equal(score, 1);
assert.equal(rawValue, 0);
assert.equal(details.items.length, 0);
Expand All @@ -100,9 +114,10 @@ describe('Performance: uses-rel-preconnect audit', () => {
devtoolsLogs: {[UsesRelPreconnect.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(networkRecords),
requestMainResource: () => Promise.resolve(mainResource),
requestLoadSimulator: () => Promise.resolve(simulator),
};

const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts);
const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts, {});
assert.equal(score, 1);
assert.equal(rawValue, 0);
assert.equal(details.items.length, 0);
Expand All @@ -121,9 +136,10 @@ describe('Performance: uses-rel-preconnect audit', () => {
devtoolsLogs: {[UsesRelPreconnect.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(networkRecords),
requestMainResource: () => Promise.resolve(mainResource),
requestLoadSimulator: () => Promise.resolve(simulator),
};

const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts);
const {score, rawValue, details} = await UsesRelPreconnect.audit(artifacts, {});
assert.equal(score, 1);
assert.equal(rawValue, 0);
assert.equal(details.items.length, 0);
Expand All @@ -136,6 +152,7 @@ describe('Performance: uses-rel-preconnect audit', () => {
url: 'https://cdn.example.com/first',
initiatorRequest: () => null,
parsedURL: {
scheme: 'https',
securityOrigin: () => 'https://cdn.example.com',
},
startTime: 2,
Expand All @@ -149,6 +166,7 @@ describe('Performance: uses-rel-preconnect audit', () => {
url: 'https://cdn.example.com/second',
initiatorRequest: () => null,
parsedURL: {
scheme: 'https',
securityOrigin: () => 'https://cdn.example.com',
},
startTime: 3,
Expand All @@ -163,13 +181,14 @@ describe('Performance: uses-rel-preconnect audit', () => {
devtoolsLogs: {[UsesRelPreconnect.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(networkRecords),
requestMainResource: () => Promise.resolve(mainResource),
requestLoadSimulator: () => Promise.resolve(simulator),
};

const {rawValue, extendedInfo} = await UsesRelPreconnect.audit(artifacts);
const {rawValue, extendedInfo} = await UsesRelPreconnect.audit(artifacts, {});
assert.equal(rawValue, 200);
assert.equal(extendedInfo.value.length, 1);
assert.deepStrictEqual(extendedInfo.value, [
{url: 'https://cdn.example.com', wastedMs: 200, type: 'ms'},
{url: 'https://cdn.example.com', wastedMs: 200},
]);
});

Expand All @@ -180,7 +199,8 @@ describe('Performance: uses-rel-preconnect audit', () => {
url: 'https://cdn.example.com/first',
initiatorRequest: () => null,
parsedURL: {
securityOrigin: () => 'https://cdn.example.com',
scheme: 'http',
securityOrigin: () => 'http://cdn.example.com',
},
startTime: 2,
_timing: {
Expand All @@ -193,6 +213,7 @@ describe('Performance: uses-rel-preconnect audit', () => {
url: 'https://othercdn.example.com/second',
initiatorRequest: () => null,
parsedURL: {
scheme: 'https',
securityOrigin: () => 'https://othercdn.example.com',
},
startTime: 1.2,
Expand All @@ -207,14 +228,20 @@ describe('Performance: uses-rel-preconnect audit', () => {
devtoolsLogs: {[UsesRelPreconnect.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(networkRecords),
requestMainResource: () => Promise.resolve(mainResource),
requestLoadSimulator: () => Promise.resolve(simulator),
};

simulatorOptions = {
rtt: 100,
additionalRttByOrigin: new Map([['https://othercdn.example.com', 50]]),
};

const {rawValue, extendedInfo} = await UsesRelPreconnect.audit(artifacts);
const {rawValue, extendedInfo} = await UsesRelPreconnect.audit(artifacts, {});
assert.equal(rawValue, 300);
assert.equal(extendedInfo.value.length, 2);
assert.deepStrictEqual(extendedInfo.value, [
{url: 'https://othercdn.example.com', wastedMs: 300, type: 'ms'},
{url: 'https://cdn.example.com', wastedMs: 200, type: 'ms'},
{url: 'https://othercdn.example.com', wastedMs: 300},
{url: 'http://cdn.example.com', wastedMs: 100},
]);
});
});
1 change: 0 additions & 1 deletion typings/gatherer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ declare global {
export interface Options {
rtt?: number;
throughput?: number;
fallbackTTFB?: number;
maximumConcurrentRequests?: number;
cpuSlowdownMultiplier?: number;
layoutTaskMultiplier?: number;
Expand Down

0 comments on commit 63698a0

Please sign in to comment.