-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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(simulator): start nodes in observed order #5362
Changes from all commits
4fca19e
d743d78
d8bbb87
ff615ea
3da10f8
f774586
b55d4bf
0567cb3
0c0a392
47af890
aa7f3bc
09c1a70
3acc949
8f03075
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,7 @@ class LanternFirstCPUIdle extends LanternInteractive { | |
longTasks.push({start: timing.startTime, end: timing.endTime}); | ||
} | ||
|
||
longTasks.sort((a, b) => a.start - b.start); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same question. can we do the sort earlier in a more generic location? I want to avoid the situation where someone writes a new audit and forgets they gotta sort on their own. |
||
// Require here to resolve circular dependency. | ||
const FirstCPUIdle = require('./first-cpu-idle'); | ||
return FirstCPUIdle.findQuietWindow(fmpTimeInMs, Infinity, longTasks); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -181,6 +181,17 @@ class Simulator { | |
}); | ||
} | ||
|
||
/** | ||
* @param {Set<Node>} nodes | ||
* @return {Node[]} | ||
*/ | ||
_getNodesSortedByStartTime(nodes) { | ||
return Array.from(nodes).sort((nodeA, nodeB) => { | ||
// Sort nodes by startTime to match original execution order | ||
return nodeA.startTime - nodeB.startTime; | ||
}); | ||
} | ||
|
||
/** | ||
* @param {Node} node | ||
* @param {number} totalElapsedTime | ||
|
@@ -354,18 +365,23 @@ class Simulator { | |
} | ||
} | ||
|
||
/** | ||
* @return {Map<Node, LH.Gatherer.Simulation.NodeTiming>} | ||
*/ | ||
_computeFinalNodeTimings() { | ||
/** @type {Map<Node, LH.Gatherer.Simulation.NodeTiming>} */ | ||
const nodeTimings = new Map(); | ||
/** @type {Array<[Node, LH.Gatherer.Simulation.NodeTiming]>} */ | ||
const nodeTimingEntries = []; | ||
for (const [node, timing] of this._nodeTimings) { | ||
nodeTimings.set(node, { | ||
nodeTimingEntries.push([node, { | ||
startTime: timing.startTime, | ||
endTime: timing.endTime, | ||
duration: timing.endTime - timing.startTime, | ||
}); | ||
}]); | ||
} | ||
|
||
return nodeTimings; | ||
// Most consumers will want the entries sorted by startTime, so insert them in that order | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❤️ |
||
nodeTimingEntries.sort((a, b) => a[1].startTime - b[1].startTime); | ||
return new Map(nodeTimingEntries); | ||
} | ||
|
||
/** | ||
|
@@ -419,7 +435,7 @@ class Simulator { | |
// loop as long as we have nodes in the queue or currently in progress | ||
while (nodesReadyToStart.size || nodesInProgress.size) { | ||
// move all possible queued nodes to in progress | ||
for (const node of nodesReadyToStart) { | ||
for (const node of this._getNodesSortedByStartTime(nodesReadyToStart)) { | ||
this._startNodeIfPossible(node, totalElapsedTime); | ||
} | ||
|
||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,4 +28,20 @@ describe('Metrics: Lantern TTFCPUI', () => { | |
assert.ok(result.optimisticGraph, 'should have created optimistic graph'); | ||
assert.ok(result.pessimisticGraph, 'should have created pessimistic graph'); | ||
}); | ||
|
||
describe('#getFirstCPUIdleWindowStart', () => { | ||
it('should sort tasks', () => { | ||
const tasks = new Map([ | ||
[{type: 'cpu'}, {startTime: 600, endTime: 700, duration: 100}], | ||
[{type: 'cpu'}, {startTime: 300, endTime: 400, duration: 100}], | ||
[{type: 'cpu'}, {startTime: 0, endTime: 100, duration: 100}], | ||
[{type: 'cpu'}, {startTime: 100, endTime: 200, duration: 100}], | ||
[{type: 'cpu'}, {startTime: 500, endTime: 600, duration: 100}], | ||
[{type: 'cpu'}, {startTime: 200, endTime: 300, duration: 100}], | ||
[{type: 'cpu'}, {startTime: 400, endTime: 500, duration: 100}], | ||
]); | ||
|
||
assert.equal(LanternFirstCPUIdle.getFirstCPUIdleWindowStart(tasks, 0), 700); | ||
}); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it might be harder to set up, but maybe a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (or maybe there's an easier way to unit test it without calling private stuff; I didn't look very hard :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't follow the flow perfectly but it seems like the new sorting in simulator would make this one redundant. But no?
(if not, could this one be done earlier within a more common location?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's redundant if the nodes all start in the order that they were queued (which used to always be true), but necessary in other cases. Even if it were true though, the fact that the
.entries
iterator on a map returns this in insertion order feels like an impl detail I wouldn't want important pieces of the codebase to rely on knowing since it bit me here once it changed slightly :/I'm generally comfortable with the idea that consumers of a Map who need the results sorted going forward need to sort it themselves. As a safeguard though, I'm cool with also sorting the map before we return it based on start time. Sound good compromise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind the double sort (which will get better when timsort in the latest V8 lands in Node), but it is going to be difficult to remember to always do this. Love this:
FWIW insertion-order iteration is called out very clearly for
Map
andSet
(unlike previous iteration order issues in JS).Maybe put a jsdoc comment on
nodeTimings
that insertion/iteration order is sorted based onstartTime
so at least the knowledge is out there somewhere?