-
Notifications
You must be signed in to change notification settings - Fork 47.1k
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
RFC Implementation: Speculative work #18262
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit c1c0529:
|
There is no doubt that it's interesting. It's a good way to provide reference value through selector and update accurately in a lazy way. But one thing, I think maybe this is not only applicable to Context, maybe we can also make props cooperate with selector? Or with a similar idea, built-in React.memo, so that components can also render accurately? And about lazy, can we do it in JSX? |
@yisar at the moment selectors for context only work in function components using the useContext hook. users would be free to incorporate props into the selector without limitation much like they can with reducers in useReducer |
This pull request has been automatically marked as stale. If this pull request is still relevant, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize reviewing it yet. Your contribution is very much appreciated. |
Closing this pull request after a prolonged period of inactivity. If this issue is still present in the latest release, please ask for this pull request to be reopened. Thank you! |
Summary
This is a long-lived PR to discuss development of an implementation for reactjs/rfcs#150
The current state is a Proof-of-Concept implementation of speculative work along with context-selectors.
There are currently four experiments embedded in this PR. some will be removed soon
selectors for useContext
flag
enableContextSelectors
this replaces
readContext
as the implementation foruseContext
and replaces with a stateful mount and update hook which can track previous values. It is not inherently 'faster' by itself since it does not affect context propagation and therefore the selector has no opportunity to bail out of work but when used withreifyNextWork
orspeculativeWork
it can help reduce the amount of work done on a render for components that only read a small part of the context valuewhat's next:
I plan on reimplementing outside of hooks so any context reader can use a selector and take advantage of more aggressive bailouts
Context propagation by Reader
flag
enableContextReaderPropagation
this feature replaces the tree-walking context propagation for one that tracks direct readers. It is analagous to each reader being a
useReducer
hook and having thedispatchAction
function held by the Provider. propagation is then mostly just dispatching a new state. It's not literally using this functionality and there are some tricks to make it faster on mount and only do the more complicated tracking in advanced cases when the set of contexts read from changes from render to render.The key benefit here is the growth in work time for propagation is O(readers) instead of O(size of tree)
what's next:
I plan on moving where we track whether we need advanced mode into the prepare step to solve some uncommon memory leaks
I plan on experimenting with a super fast mode where we only activate reader tracking after a provider has a changed value at least once. this would enable passive contexts to add no mount or unmount costs
Reify Next Work
flag
enableReifyNextWork
This is the primary feature of Speculative Work. When this flag is enabled the bailoutOnAlreadyCompleted work will be modified so that before deciding whether to go deeper into the subtree the boundary of the next fibers that have work will be visited with a walking algorithm similar to context propagation. when a fiber with work is found and if that fiber type is supported (FunctionComponent for instance) a semi-eager bailout can run which will determine if that speculative update is now reified? (i.e. an update went from "this might need to an update" to "there is no update" or "there is definitely" an update)
If reification happens we've hit a work boundary and move on. once we exhaust the sub-tree up to the next work boudary and return back to our bailout we see if there is still any work to do deeper. the rest of the bailout logic remains unchanged
this is superior to speculative mode because the tree walking algo is lighter since we aren't doing the full 'work' logic, and because it is simpler in that we don't have to juggle "are we doing work on current or on work in progress"
what's next:
explore doing a full render call during reifyNextWork and stashing the result. if work is needed we can use the precomputed workInProgress instead of doing it a second time. this is actually 'safe' because we no none of the intermediate work could have affected the render
Also look into how we might support class components
DEPRECATED Speculative Work Mode
flag
enableSpeculativeWork
This was my initial implementation of speculative work and it was good but
reifyNextWork
is better. Essentially if we did a bailoutOnAlreadyCompleted work we would not create workInProgress fibers for children but would instead start work on the current tree. Then if we got somewhere where an update on curren required workInProgress we would reify the ancestor current tree back to the last WIP fiber before we started speculative mode and continue from there.This worked and did offer some performance enhancements in some cases but there are a lot of extra edge cases tracking whether you are in speculative mode or not and how to handle switching between those modes
This feature is technically compatible with reifyNextWork but doesn't really offer any additional performance advantages and so it should probably be abandoned
What's next:
remove from codebase