-
Notifications
You must be signed in to change notification settings - Fork 46.2k
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
[Scheduler] Store Tasks on a Min Binary Heap #16245
Conversation
React: size: -1.4%, gzip: 0.0% Details of bundled changes.Comparing: 95767ac...f2318c1 react
scheduler
Generated by 🚫 dangerJS |
4760cbc
to
9bead1f
Compare
@@ -0,0 +1,92 @@ | |||
/** |
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.
Does this mean we've given up on having a single easy to reason about file?
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.
Personally I don’t think it’s worth it but I can move this into the same file if you want me to
To maximize adoption, I think polyfilling the native API is the better approach.
bd83da5
to
0e2c76a
Compare
if (typeof continuationCallback === 'function') { | ||
var expirationTime = task.expirationTime; |
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.
This whole branch is left over from before the "return a continuation" API existed. Instead of popping the task from the queue, checking if there's a continuation, and then rescheduling it, we can peek at the task and only remove it once it completes. If there's a continuation, then the task remains in its current position.
|
||
function siftUp(heap, node, index) { | ||
while (index > 0) { | ||
const parentIndex = Math.floor((index + 1) / 2) - 1; |
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.
Math.floor((index - 1) / 2)
is equivalent and simpler?
const first = heap[0]; | ||
if (first !== undefined) { | ||
const last = heap.pop(); | ||
if (last !== undefined && last !== first) { |
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.
last === undefined
never happens, right?
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.
Yeah I think maybe this was to satisfy Flow? Will check
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.
Nevermind, forgot Flow array access is intentionally unsafe for exactly this reason :D
|
||
function siftDown(heap, node, index) { | ||
const length = heap.length; | ||
while (index < length) { |
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.
This is never false, right? Should it just be while (true)
?
task.next = task.previous = null; | ||
// Null out the callback to indicate the task has been canceled. (Can't remove | ||
// from the queue because you can't remove arbitrary nodes from an array based | ||
// heap, only the first one.) |
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.
Can't you sift up/down as appropriate?
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 think so because I don’t know the index of the task.
Will wait to merge this until after 16.9 is released |
Switches Scheduler's priority queue implementation (for both tasks and timers) to an array-based min binary heap. This replaces the naive linked-list implementation that was left over from the queue we once used to schedule React roots. A list was arguably fine when it was only used for roots, since the total number of roots is usually small, and is only 1 in the common case of a single-page app. Since Scheduler is now used for many types of JavaScript tasks (e.g. including timers), the total number of tasks can be much larger. Binary heaps are the standard way to implement priority queues. Insertion is O(1) in the average case (append to the end) and O(log n) in the worst. Deletion is O(log n). Peek is O(1).
0e2c76a
to
a5a769f
Compare
a5a769f
to
f2318c1
Compare
Switches Scheduler's priority queue implementation (for both tasks and timers) to an array-based min binary heap.
This replaces the naive linked-list implementation that was left over from the queue we once used to schedule React roots. A list was arguably fine when it was only used for roots, since the total number of roots is usually small, and is only 1 in the common case of a single-page app.
Since Scheduler is now used for many types of JavaScript tasks (e.g. including timers), the total number of tasks can be much larger.
Heaps are the standard way to implement priority queues. Insertion is O(1) in the average case (append to the end) and O(log n) in the worst. Deletion is O(log n). Peek is O(1).