Skip to content

Commit

Permalink
core(tsc): make CPUNode and NetworkNode a discriminated union (#5548)
Browse files Browse the repository at this point in the history
  • Loading branch information
brendankenny authored Jun 26, 2018
1 parent cf2e13a commit 9c5b76c
Show file tree
Hide file tree
Showing 17 changed files with 183 additions and 194 deletions.
21 changes: 9 additions & 12 deletions lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@

const Audit = require('../audit');
const linearInterpolation = require('../../lib/statistics').linearInterpolation;
const Interactive = require('../../gather/computed/metrics/lantern-interactive'); // eslint-disable-line max-len
const Simulator = require('../../lib/dependency-graph/simulator/simulator'); // eslint-disable-line no-unused-vars
const Node = require('../../lib/dependency-graph/node.js'); // eslint-disable-line no-unused-vars
const Interactive = require('../../gather/computed/metrics/lantern-interactive');

const NetworkNode = require('../../lib/dependency-graph/network-node.js'); // eslint-disable-line no-unused-vars
/** @typedef {import('../../lib/dependency-graph/simulator/simulator')} Simulator */
/** @typedef {import('../../lib/dependency-graph/base-node.js').Node} Node */

const KB_IN_BYTES = 1024;

Expand Down Expand Up @@ -148,26 +147,24 @@ class UnusedBytes extends Audit {
const originalTransferSizes = new Map();
graph.traverse(node => {
if (node.type !== 'network') return;
const networkNode = /** @type {NetworkNode} */ (node);
const result = resultsByUrl.get(networkNode.record.url);
const result = resultsByUrl.get(node.record.url);
if (!result) return;

const original = networkNode.record.transferSize;
originalTransferSizes.set(networkNode.record.requestId, original);
const original = node.record.transferSize;
originalTransferSizes.set(node.record.requestId, original);

const wastedBytes = result.wastedBytes;
networkNode.record.transferSize = Math.max(original - wastedBytes, 0);
node.record.transferSize = Math.max(original - wastedBytes, 0);
});

const simulationAfterChanges = simulator.simulate(graph, {label: afterLabel});

// Restore the original transfer size after we've done our simulation
graph.traverse(node => {
if (node.type !== 'network') return;
const networkNode = /** @type {NetworkNode} */ (node);
const originalTransferSize = originalTransferSizes.get(networkNode.record.requestId);
const originalTransferSize = originalTransferSizes.get(node.record.requestId);
if (originalTransferSize === undefined) return;
networkNode.record.transferSize = originalTransferSize;
node.record.transferSize = originalTransferSize;
});

const savingsOnOverallLoad = simulationBeforeChanges.timeInMs - simulationAfterChanges.timeInMs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
'use strict';

const Audit = require('../audit');
const Node = require('../../lib/dependency-graph/node');
const BaseNode = require('../../lib/dependency-graph/base-node');
const ByteEfficiencyAudit = require('./byte-efficiency-audit');
const UnusedCSS = require('./unused-css-rules');
const WebInspector = require('../../lib/web-inspector');

const Simulator = require('../../lib/dependency-graph/simulator/simulator'); // eslint-disable-line no-unused-vars
const NetworkNode = require('../../lib/dependency-graph/network-node.js'); // eslint-disable-line no-unused-vars
/** @typedef {import('../../lib/dependency-graph/simulator/simulator')} Simulator */
/** @typedef {import('../../lib/dependency-graph/base-node.js').Node} Node */
/** @typedef {import('../../lib/dependency-graph/network-node.js')} NetworkNode */

// Because of the way we detect blocking stylesheets, asynchronously loaded
// CSS with link[rel=preload] and an onload handler (see https://github.com/filamentgroup/loadCSS)
Expand All @@ -35,11 +36,10 @@ function getNodesAndTimingByUrl(nodeTimings) {
const nodes = Array.from(nodeTimings.keys());
nodes.forEach(node => {
if (node.type !== 'network') return;
const networkNode = /** @type {NetworkNode} */ (node);
const nodeTiming = nodeTimings.get(node);
if (!nodeTiming) return;

urlMap[networkNode.record.url] = {node, nodeTiming};
urlMap[node.record.url] = {node, nodeTiming};
});

return urlMap;
Expand Down Expand Up @@ -149,16 +149,14 @@ class RenderBlockingResources extends Audit {
const minimalFCPGraph = /** @type {NetworkNode} */ (fcpGraph.cloneWithRelationships(node => {
// If a node can be deferred, exclude it from the new FCP graph
const canDeferRequest = deferredIds.has(node.id);
if (node.type !== Node.TYPES.NETWORK) return !canDeferRequest;

const networkNode = /** @type {NetworkNode} */ (node);
if (node.type !== BaseNode.TYPES.NETWORK) return !canDeferRequest;

const isStylesheet =
networkNode.record._resourceType === WebInspector.resourceTypes.Stylesheet;
node.record._resourceType === WebInspector.resourceTypes.Stylesheet;
if (canDeferRequest && isStylesheet) {
// We'll inline the used bytes of the stylesheet and assume the rest can be deferred
const wastedBytes = wastedCssBytesByUrl.get(networkNode.record.url) || 0;
totalChildNetworkBytes += (networkNode.record.transferSize || 0) - wastedBytes;
const wastedBytes = wastedCssBytesByUrl.get(node.record.url) || 0;
totalChildNetworkBytes += (node.record.transferSize || 0) - wastedBytes;
}
return !canDeferRequest;
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
'use strict';

const LanternMetricArtifact = require('./lantern-metric');
const Node = require('../../../lib/dependency-graph/node');
const BaseNode = require('../../../lib/dependency-graph/base-node');
const EstimatedInputLatency = require('./estimated-input-latency');

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

class LanternEstimatedInputLatency extends LanternMetricArtifact {
get name() {
return 'LanternEstimatedInputLatency';
Expand Down Expand Up @@ -82,7 +84,7 @@ class LanternEstimatedInputLatency extends LanternMetricArtifact {
/** @type {Array<{start: number, end: number, duration: number}>} */
const events = [];
for (const [node, timing] of nodeTimings.entries()) {
if (node.type !== Node.TYPES.CPU) continue;
if (node.type !== BaseNode.TYPES.CPU) continue;
if (timing.endTime < fmpTimeInMs) continue;

events.push({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
'use strict';

const MetricArtifact = require('./lantern-metric');
const Node = require('../../../lib/dependency-graph/node');
const CPUNode = require('../../../lib/dependency-graph/cpu-node'); // eslint-disable-line no-unused-vars
const NetworkNode = require('../../../lib/dependency-graph/network-node'); // eslint-disable-line no-unused-vars
const BaseNode = require('../../../lib/dependency-graph/base-node');

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

class FirstContentfulPaint extends MetricArtifact {
get name() {
Expand Down Expand Up @@ -42,13 +42,12 @@ class FirstContentfulPaint extends MetricArtifact {
return dependencyGraph.cloneWithRelationships(node => {
if (node.endTime > fcp && !node.isMainDocument()) return false;
// Include EvaluateScript tasks for blocking scripts
if (node.type === Node.TYPES.CPU) {
return /** @type {CPUNode} */ (node).isEvaluateScriptFor(blockingScriptUrls);
if (node.type === BaseNode.TYPES.CPU) {
return node.isEvaluateScriptFor(blockingScriptUrls);
}

const asNetworkNode = /** @type {NetworkNode} */ (node);
// Include non-script-initiated network requests with a render-blocking priority
return asNetworkNode.hasRenderBlockingPriority() && asNetworkNode.initiatorType !== 'script';
return node.hasRenderBlockingPriority() && node.initiatorType !== 'script';
});
}

Expand All @@ -66,12 +65,12 @@ class FirstContentfulPaint extends MetricArtifact {
return dependencyGraph.cloneWithRelationships(node => {
if (node.endTime > fcp && !node.isMainDocument()) return false;
// Include EvaluateScript tasks for blocking scripts
if (node.type === Node.TYPES.CPU) {
return /** @type {CPUNode} */ (node).isEvaluateScriptFor(blockingScriptUrls);
if (node.type === BaseNode.TYPES.CPU) {
return node.isEvaluateScriptFor(blockingScriptUrls);
}

// Include non-script-initiated network requests with a render-blocking priority
return /** @type {NetworkNode} */ (node).hasRenderBlockingPriority();
return node.hasRenderBlockingPriority();
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
*/
'use strict';

const Node = require('../../../lib/dependency-graph/node');
const CPUNode = require('../../../lib/dependency-graph/cpu-node'); // eslint-disable-line no-unused-vars
const NetworkNode = require('../../../lib/dependency-graph/network-node'); // eslint-disable-line no-unused-vars

const BaseNode = require('../../../lib/dependency-graph/base-node');
const FirstCPUIdle = require('./first-cpu-idle');
const LanternInteractive = require('./lantern-interactive');

Expand Down Expand Up @@ -42,7 +39,7 @@ class LanternFirstCPUIdle extends LanternInteractive {
/** @type {Array<{start: number, end: number}>} */
const longTasks = [];
for (const [node, timing] of nodeTimings.entries()) {
if (node.type !== Node.TYPES.CPU) continue;
if (node.type !== BaseNode.TYPES.CPU) continue;
if (timing.duration < longTaskLength) continue;
longTasks.push({start: timing.startTime, end: timing.endTime});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
'use strict';

const MetricArtifact = require('./lantern-metric');
const Node = require('../../../lib/dependency-graph/node');
const CPUNode = require('../../../lib/dependency-graph/cpu-node'); // eslint-disable-line no-unused-vars
const NetworkNode = require('../../../lib/dependency-graph/network-node'); // eslint-disable-line no-unused-vars
const BaseNode = require('../../../lib/dependency-graph/base-node');

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

class FirstMeaningfulPaint extends MetricArtifact {
get name() {
Expand Down Expand Up @@ -42,13 +42,12 @@ class FirstMeaningfulPaint extends MetricArtifact {
return dependencyGraph.cloneWithRelationships(node => {
if (node.endTime > fmp && !node.isMainDocument()) return false;
// Include EvaluateScript tasks for blocking scripts
if (node.type === Node.TYPES.CPU) {
return /** @type {CPUNode} */ (node).isEvaluateScriptFor(blockingScriptUrls);
if (node.type === BaseNode.TYPES.CPU) {
return node.isEvaluateScriptFor(blockingScriptUrls);
}

const asNetworkNode = /** @type {NetworkNode} */ (node);
// Include non-script-initiated network requests with a render-blocking priority
return asNetworkNode.hasRenderBlockingPriority() && asNetworkNode.initiatorType !== 'script';
return node.hasRenderBlockingPriority() && node.initiatorType !== 'script';
});
}

Expand All @@ -67,13 +66,12 @@ class FirstMeaningfulPaint extends MetricArtifact {
if (node.endTime > fmp && !node.isMainDocument()) return false;

// Include CPU tasks that performed a layout or were evaluations of required scripts
if (node.type === Node.TYPES.CPU) {
const asCpuNode = /** @type {CPUNode} */ (node);
return asCpuNode.didPerformLayout() || asCpuNode.isEvaluateScriptFor(requiredScriptUrls);
if (node.type === BaseNode.TYPES.CPU) {
return node.didPerformLayout() || node.isEvaluateScriptFor(requiredScriptUrls);
}

// Include all network requests that had render-blocking priority (even script-initiated)
return /** @type {NetworkNode} */ (node).hasRenderBlockingPriority();
return node.hasRenderBlockingPriority();
});
}

Expand Down
21 changes: 10 additions & 11 deletions lighthouse-core/gather/computed/metrics/lantern-interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
'use strict';

const MetricArtifact = require('./lantern-metric');
const Node = require('../../../lib/dependency-graph/node');
const CPUNode = require('../../../lib/dependency-graph/cpu-node'); // eslint-disable-line no-unused-vars
const NetworkNode = require('../../../lib/dependency-graph/network-node'); // eslint-disable-line no-unused-vars
const BaseNode = require('../../../lib/dependency-graph/base-node');
const WebInspector = require('../../../lib/web-inspector');

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

// Any CPU task of 20 ms or more will end up being a critical long task on mobile
const CRITICAL_LONG_TASK_THRESHOLD = 20;

Expand Down Expand Up @@ -40,19 +40,18 @@ class Interactive extends MetricArtifact {

return dependencyGraph.cloneWithRelationships(node => {
// Include everything that might be a long task
if (node.type === Node.TYPES.CPU) {
return /** @type {CPUNode} */ (node).event.dur > minimumCpuTaskDuration;
if (node.type === BaseNode.TYPES.CPU) {
return node.event.dur > minimumCpuTaskDuration;
}

const asNetworkNode = /** @type {NetworkNode} */ (node);
// Include all scripts and high priority requests, exclude all images
const isImage = asNetworkNode.record._resourceType === WebInspector.resourceTypes.Image;
const isScript = asNetworkNode.record._resourceType === WebInspector.resourceTypes.Script;
const isImage = node.record._resourceType === WebInspector.resourceTypes.Image;
const isScript = node.record._resourceType === WebInspector.resourceTypes.Script;
return (
!isImage &&
(isScript ||
asNetworkNode.record.priority() === 'High' ||
asNetworkNode.record.priority() === 'VeryHigh')
node.record.priority() === 'High' ||
node.record.priority() === 'VeryHigh')
);
});
}
Expand Down Expand Up @@ -101,7 +100,7 @@ class Interactive extends MetricArtifact {
// @ts-ignore TS can't infer how the object invariants change
return Array.from(nodeTimings.entries())
.filter(([node, timing]) => {
if (node.type !== Node.TYPES.CPU) return false;
if (node.type !== BaseNode.TYPES.CPU) return false;
return timing.duration > duration;
})
.map(([_, timing]) => timing.endTime)
Expand Down
20 changes: 11 additions & 9 deletions lighthouse-core/gather/computed/metrics/lantern-metric.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
'use strict';

const ComputedArtifact = require('../computed-artifact');
const Node = require('../../../lib/dependency-graph/node');
const NetworkNode = require('../../../lib/dependency-graph/network-node'); // eslint-disable-line no-unused-vars
const Simulator = require('../../../lib/dependency-graph/simulator/simulator'); // eslint-disable-line no-unused-vars
const WebInspector = require('../../../lib/web-inspector');
const BaseNode = require('../../../lib/dependency-graph/base-node');
const ResourceType = require('../../../../third-party/devtools/ResourceType');

/** @typedef {BaseNode.Node} Node */
/** @typedef {import('../../../lib/dependency-graph/network-node')} NetworkNode */
/** @typedef {import('../../../lib/dependency-graph/simulator/simulator')} Simulator */

class LanternMetricArtifact extends ComputedArtifact {
/**
Expand All @@ -18,14 +20,14 @@ class LanternMetricArtifact extends ComputedArtifact {
* @return {Set<string>}
*/
static getScriptUrls(dependencyGraph, condition) {
/** @type {Set<string>} */
const scriptUrls = new Set();

dependencyGraph.traverse(node => {
if (node.type === Node.TYPES.CPU) return;
const asNetworkNode = /** @type {NetworkNode} */ (node);
if (asNetworkNode.record._resourceType !== WebInspector.resourceTypes.Script) return;
if (condition && !condition(asNetworkNode)) return;
scriptUrls.add(asNetworkNode.record.url);
if (node.type === BaseNode.TYPES.CPU) return;
if (node.record._resourceType !== ResourceType.TYPES.Script) return;
if (condition && !condition(node)) return;
scriptUrls.add(node.record.url);
});

return scriptUrls;
Expand Down
10 changes: 5 additions & 5 deletions lighthouse-core/gather/computed/metrics/lantern-speed-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
'use strict';

const MetricArtifact = require('./lantern-metric');
const Node = require('../../../lib/dependency-graph/node');
const CPUNode = require('../../../lib/dependency-graph/cpu-node'); // eslint-disable-line no-unused-vars
const BaseNode = require('../../../lib/dependency-graph/base-node');

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

class SpeedIndex extends MetricArtifact {
get name() {
Expand Down Expand Up @@ -95,10 +96,9 @@ class SpeedIndex extends MetricArtifact {
/** @type {Array<{time: number, weight: number}>} */
const layoutWeights = [];
for (const [node, timing] of nodeTimings.entries()) {
if (node.type !== Node.TYPES.CPU) continue;
if (node.type !== BaseNode.TYPES.CPU) continue;

const cpuNode = /** @type {CPUNode} */ (node);
if (cpuNode.childEvents.some(x => x.name === 'Layout')) {
if (node.childEvents.some(x => x.name === 'Layout')) {
const timingWeight = Math.max(Math.log2(timing.endTime - timing.startTime), 0);
layoutWeights.push({time: timing.endTime, weight: timingWeight});
}
Expand Down
2 changes: 1 addition & 1 deletion lighthouse-core/gather/computed/page-dependency-graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const NetworkAnalyzer = require('../../lib/dependency-graph/simulator/network-an
const TracingProcessor = require('../../lib/traces/tracing-processor');
const WebInspector = require('../../lib/web-inspector');

const Node = require('../../lib/dependency-graph/node.js'); // eslint-disable-line no-unused-vars
/** @typedef {import('../../lib/dependency-graph/base-node.js').Node} Node */

// Tasks smaller than 10 ms have minimal impact on simulation
const MINIMUM_TASK_DURATION_OF_INTEREST = 10;
Expand Down
Loading

0 comments on commit 9c5b76c

Please sign in to comment.