-
-
Notifications
You must be signed in to change notification settings - Fork 8.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
Refactor reactivity system to use version counting and doubly-linked list tracking #10397
Conversation
/ecosystem-ci run |
📝 Ran ecosystem CI: Open
|
The snapshot fail for Vant should be expected - the /cc @chenjiahan |
Get it 👌 I will temporarily skip the failed test cases in Vant until the next version of Vue is upgraded. |
Congratulations! You have finally passed the tests for the absence of unnecessary side effects and your reactivity system is suitable for use in production. Unfortunately, you have not yet got rid of unnecessary recalculations. @preact/signals is not the best option to follow here. |
@nin-jin our priority is making improvements while retaining backwards compatibility, not "winning" in arbitrary micro-benchmarks. We will appreciate any constructive feedback, but being a tongue-in-cheek jerk won't help your library get more adoption. |
Don't be offended by my praise. Accept it with pride. But don't get too cocky. A year ago, in my analysis, I classified your library as unsuitable for production. In this patch, you fixed it. Now I should update my analysis and move you significantly higher. But if you read my series of articles on reactivity, you may understand how to catch up with the leaders without even breaking backward compatibility. |
They tell me here that you use a double double linked list to implement pub/sub. This is extremely wasteful of memory (from 44 bytes per edge) and heavily fragments it, which increases processor misses past caches. In a recent article on pub/sub, I talked about how to achieve a more compact storage of edges in single array (16 bytes per edge) while ensuring constant asymptotic of all operations. |
@nin-jin The result seems to be based on Vue 3.4.x rather than this PR. I'm also curious as to why 3.4 produces unnecessary recalculations, and I'd be happy to try to fix it. (I am the author of the Vue 3.4 responsive refactoring PR) |
Hello yyx, I read your pr in detail, thx your this amazing work! |
@moushicheng Try to answer your question(I haven't read this change carefully yet. I plan to spend some time to finish reading the source code of In the current version of |
Some of it is likely only relevant to Preact signals, and not necessarily this change here in Vue, but there's a blog article going over why we (Preact) used linked lists (amongst other things) here: https://preactjs.com/blog/signal-boosting |
This PR refactors the core reactivity system to use version counting and a doubly-linked list data structure inspired by Preact signals.
It brings some improvements and solves some issues around computed.
Bug fixes
close #10236
close #10069
PRs made stale by this one
close #10290
close #10354
close #10189
close #9480
@vue/reactivity
API surface changesdiff
These are changes to the
@vue/reactivity
package only and do not affect Vue core's public API surface.pauseScheduling
andresetScheduling
exports (never publicly documented)deferredComputed
export (previously deprecated)EffectFlags
export (for Vue core internal use)Memory Usage Improvements
Given a test case with 1000 refs + 2000 computeds (1000 chained pairs) + 1000 effects subscribing to the last computed, comparing the total memory used by these class instances:
A computed now also lazily subscribe to deps only when itself gets the first subscriber, and unsubscribes when it loses all subscribers. This means they are more reliably garbage-collected, including in SSR.
Performance Comparison
Reasonable gains across the board, most notably for single ref invoking multiple effects (+118%~185%) and reading multiple invalidated computeds (+176%~244%).
Full benchmarks for 3.4.19
Full benchmarks for this PR
Computed value are now never stale
Previously computed are internally driven by effects. The effects are stopped / made inactive when the owner unmounts, and in 3.4 leading to issues like #10236.
Also, previously during SSR we disable computed effects to avoid tracking overhead. But to avoid cost of repeated computation, we make computeds cached during render, leading to issues like #10069.
With this PR, computeds are now a subscriber type with its own invalidation logic. They can longer be "stopped" and thus can never be stale. During SSR, we use a global mutation version counter to provide a fast path for repeated access.