Skip to content

Commit

Permalink
fix(predictive-perf): split CPU and Layout task multipliers
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickhulce committed Aug 28, 2017
1 parent 972a935 commit b1fde39
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 14 deletions.
Expand Up @@ -10,10 +10,18 @@ const TcpConnection = require('./tcp-connection');

// see https://cs.chromium.org/search/?q=kDefaultMaxNumDelayableRequestsPerClient&sq=package:chromium&type=cs
const DEFAULT_MAXIMUM_CONCURRENT_REQUESTS = 10;

// Fast 3G emulation target from DevTools, WPT 3G - Fast setting
const DEFAULT_RESPONSE_TIME = 30;
const DEFAULT_RTT = 150;
const DEFAULT_THROUGHPUT = 1600 * 1024; // 1.6 Mbps
const DEFAULT_CPU_MULTIPLIER = 5;

// same multiplier as Lighthouse uses for CPU emulation
const DEFAULT_CPU_TASK_MULTIPLIER = 4;
// layout tasks tend to be less CPU-bound and do not experience the same increase in duration
const DEFAULT_LAYOUT_TASK_MULTIPLIER = 2;
// if a task takes more than 10 seconds it's usually a sign it isn't actually CPU bound and we're over estimating
const DEFAULT_MAXIMUM_CPU_TASK_DURATION = 10000;

function groupBy(items, keyFunc) {
const grouped = new Map();
Expand Down Expand Up @@ -41,7 +49,8 @@ class Estimator {
throughput: DEFAULT_THROUGHPUT,
defaultResponseTime: DEFAULT_RESPONSE_TIME,
maximumConcurrentRequests: DEFAULT_MAXIMUM_CONCURRENT_REQUESTS,
cpuMultiplier: DEFAULT_CPU_MULTIPLIER,
cpuTaskMultiplier: DEFAULT_CPU_TASK_MULTIPLIER,
layoutTaskMultiplier: DEFAULT_LAYOUT_TASK_MULTIPLIER,
},
options
);
Expand All @@ -53,7 +62,8 @@ class Estimator {
TcpConnection.maximumSaturatedConnections(this._rtt, this._throughput),
this._options.maximumConcurrentRequests
);
this._cpuMultiplier = this._options.cpuMultiplier;
this._cpuTaskMultiplier = this._options.cpuTaskMultiplier;
this._layoutTaskMultiplier = this._options.layoutTaskMultiplier;
}

/**
Expand Down Expand Up @@ -227,7 +237,13 @@ class Estimator {
_estimateTimeRemaining(node) {
if (node.type === Node.TYPES.CPU) {
const auxData = this._nodeAuxiliaryData.get(node);
const totalDuration = Math.round(node.event.dur / 1000 * this._cpuMultiplier);
const multiplier = node.didPerformLayout()
? this._layoutTaskMultiplier
: this._cpuTaskMultiplier;
const totalDuration = Math.min(
Math.round(node.event.dur / 1000 * multiplier),
DEFAULT_MAXIMUM_CPU_TASK_DURATION
);
const estimatedTimeElapsed = totalDuration - auxData.timeElapsed;
this._setAuxData(node, {estimatedTimeElapsed});
return estimatedTimeElapsed;
Expand Down
6 changes: 5 additions & 1 deletion lighthouse-core/gather/computed/page-dependency-graph.js
Expand Up @@ -11,7 +11,10 @@ const CPUNode = require('./dependency-graph/cpu-node');
const GraphEstimator = require('./dependency-graph/estimator/estimator');
const TracingProcessor = require('../../lib/traces/tracing-processor');

const MINIMUM_TASK_DURATION = 10 * 1000;
// tasks shorter than 10 ms are unlikely have a significant impact
const MINIMUM_TASK_DURATION = 10000;
// video files tend to be enormous and throw off all graph traversals
const IGNORED_MIME_TYPES_REGEX = /^video/;

class PageDependencyGraphArtifact extends ComputedArtifact {
get name() {
Expand Down Expand Up @@ -47,6 +50,7 @@ class PageDependencyGraphArtifact extends ComputedArtifact {
const urlToNodeMap = new Map();

networkRecords.forEach(record => {
if (IGNORED_MIME_TYPES_REGEX.test(record.mimeType)) return;
const node = new NetworkNode(record);
nodes.push(node);
idToNodeMap.set(record.requestId, node);
Expand Down
12 changes: 6 additions & 6 deletions lighthouse-core/test/audits/predictive-perf-test.js
Expand Up @@ -25,15 +25,15 @@ describe('Performance: predictive performance audit', () => {
}, Runner.instantiateComputedArtifacts());

return PredictivePerf.audit(artifacts).then(output => {
assert.equal(output.score, 96);
assert.equal(Math.round(output.rawValue), 2453);
assert.equal(output.displayValue, '2,450\xa0ms');
assert.equal(output.score, 99);
assert.equal(Math.round(output.rawValue), 1696);
assert.equal(output.displayValue, '1,700\xa0ms');

const valueOf = name => Math.round(output.extendedInfo.value[name]);
assert.equal(valueOf('optimisticFMP'), 754);
assert.equal(valueOf('pessimisticFMP'), 2070);
assert.equal(valueOf('optimisticTTCI'), 3340);
assert.equal(valueOf('pessimisticTTCI'), 3649);
assert.equal(valueOf('pessimisticFMP'), 1191);
assert.equal(valueOf('optimisticTTCI'), 2438);
assert.equal(valueOf('pessimisticTTCI'), 2399);
});
});
});
Expand Up @@ -51,7 +51,10 @@ describe('DependencyGraph/Estimator', () => {
const cpuNode = new CpuNode(cpuTask({duration: 200}));
cpuNode.addDependency(rootNode);

const estimator = new Estimator(rootNode, {defaultResponseTime: 500});
const estimator = new Estimator(rootNode, {
defaultResponseTime: 500,
cpuTaskMultiplier: 5,
});
const result = estimator.estimate();
// should be 2 RTTs and 500ms for the server response time + 200 CPU
assert.equal(result, 300 + 500 + 200);
Expand Down Expand Up @@ -83,7 +86,10 @@ describe('DependencyGraph/Estimator', () => {
nodeA.addDependent(nodeC);
nodeA.addDependent(nodeD);

const estimator = new Estimator(nodeA, {defaultResponseTime: 500});
const estimator = new Estimator(nodeA, {
defaultResponseTime: 500,
cpuTaskMultiplier: 5,
});
const result = estimator.estimate();
// should be 800ms A, then 1000 ms total for B, C, D in serial
assert.equal(result, 1800);
Expand All @@ -103,7 +109,10 @@ describe('DependencyGraph/Estimator', () => {
nodeC.addDependent(nodeD);
nodeC.addDependent(nodeF); // finishes 400 ms after D

const estimator = new Estimator(nodeA, {defaultResponseTime: 500});
const estimator = new Estimator(nodeA, {
defaultResponseTime: 500,
cpuTaskMultiplier: 5,
});
const result = estimator.estimate();
// should be 800ms each for A, B, C, D, with F finishing 400 ms after D
assert.equal(result, 3600);
Expand Down

0 comments on commit b1fde39

Please sign in to comment.