-
Notifications
You must be signed in to change notification settings - Fork 4.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
Perf: Rely on more efficient Map for Priority Queue internal state #47156
Conversation
Size Change: -68 B (0%) Total Size: 1.33 MB
ℹ️ View Unchanged
|
isRunning = false; | ||
return; | ||
for ( const [ nextElement, callback ] of waitingList ) { | ||
waitingList.delete( nextElement ); |
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.
Is it ok to delete an element from a map while we continue iterating. We won't be skipping elements randomly or something? I guess not since the tests look ok, but that's the only weird thing here.
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 verified this before adding it; also, if we delete and element and then add it back it will appear at the end of the iteration since it was added last, even though it existed in the map when iteration began.
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 looks like a nice simplification, I was wondering why I've used weak map initially. I guess I wanted to "drop" the item if the object memory is cleared elsewhere but since the object was kept in the waiting list array, this was not being done anyway.
307cef9
to
1c25ee4
Compare
1c25ee4
to
1d29e0a
Compare
…47156) The priority queue has been maintaining an array of enqueued contexts and a `WeakMap` of enqueued callbacks for dispatching idle updates. Priority ordering of context is maintained through the inherent order of the array, but this comes at the cost of calling `.shift()` on every dispatch, which rewrites the ordering of all the remaining keys or indices of the array. When the priority queue gets large (such as in store updates) this can incur a measurable cost in time. Additionally, the use of the array for ordering relies on `WeakMap` semantics to loosely hold references to the contexts, which might be removed. In this patch we're relying solely on a single `Map`. By spec, `Map` iteration follows the order of first insertion of its keys, which exactly matches the needs of the priority queue iteration. We're able to avoid using the `Array` altogether, and while not totally free, `Map` deletion is measurably faster than `Array.prototype.shift()` and will involve at least one fewer references to the context memory than having it in the array _and_ the `WeakMap` did. The goal of this patch is small and likely hard to measure. It aims to lower memory allocation and pressure on the garbage collector during the critical hot-patch of code which runs on every keypress or change in the editor, so being a little better here will hopefully add up to macro-level improvements in editor responsiveness.
1d29e0a
to
14e8390
Compare
What?
Small performance refactor in priority queue.
Why?
This is targeting code in the critical hot path following keypresses and editor changes. It's aim is to guard the editor responsiveness.
How?
The priority queue has been maintaining an array of enqueued contexts and a
WeakMap
of enqueued callbacks for dispatching idle updates.Priority ordering of context is maintained through the inherent order of the array, but this comes at the cost of calling
.shift()
on every dispatch, which rewrites the ordering of all the remaining keys or indices of the array. When the priority queue gets large (such as in store updates) this can incur a measurable cost in time. Additionally, the use of the array for ordering relies onWeakMap
semantics to loosely hold references to the contexts, which might be removed.In this patch we're relying solely on a single
Map
. By spec,Map
iteration follows the order of first insertion of its keys, which exactly matches the needs of the priority queue iteration. We're able to avoid using theArray
altogether, and while not totally free,Map
deletion is measurably faster thanArray.prototype.shift()
and will involve at least one fewer references to the context memory than having it in the array and theWeakMap
did.The goal of this patch is small and likely hard to measure. It aims to lower memory allocation and pressure on the garbage collector during the critical hot-patch of code which runs on every keypress or change in the editor, so being a little better here will hopefully add up to macro-level improvements in editor responsiveness.
Testing Instructions
Assuming this is covered by the existing test cases, please audit the code and look for discrepancies in the refactor which might change the behavior of the component. It's heavily used through the editor and so should be vetted with extra scrutiny.