-
Notifications
You must be signed in to change notification settings - Fork 1.2k
using selectorFamily in a loop is slow #914
Comments
The You may also want to try skipping the |
@drarmstr thank you for your answer. Please, keep in mind that this is a minimal working example - thats why you feel that the selector does nothing. The way to fix that on our side, was to keep all the byId state inside one atom (not a family) with custom memoization. But, this is weird since it should have been handled by the recoil in the first place. |
@novykh I was able to replicate your concern with the snippets you sent, and have a theory on the root cause which should help in instrumenting a fix. For my analysis, I attempted some variations of accessing all items from your resourcesByIdAtom. The first was using your resourcesState selector family: const loadResourcesHelper = useRecoilCallback(({snapshot}) => () => {
return snapshot.getLoadable(resourcesState(snapshot.getLoadable(resourceIds).contents)).contents;
}) I admit I was not impressed by the results, as accessing 1000 atoms took almost a second. Yet alone the 5000 I attempted the benchmark with. Second I used the resourceState selector family: const loadResourcesSelector = useRecoilCallback(({snapshot}) => () => {
return snapshot.getLoadable(resourceIds).contents.map((id) => snapshot.getLoadable(resourceState(id)).contents);
}) This implementation ran much faster easily passing a reasonable benchmark of 5000 atoms in mere milliseconds. Third I used the resourcesByIdAtom directly: const loadResourcesDirectly = useRecoilCallback(({snapshot}) => () => {
return snapshot.getLoadable(resourceIds).contents.map((id) => snapshot.getLoadable(resourcesByIdAtom(id)).contents);
}) This implementation was indistinguishable from the second implementation. The results suggest that you are correct there is an issue with the implementation of the resources state selector family. The examples I show suggest that by extracting the map outside of the selector you should be able to resolve the performance issues. |
Could it be related to dependency tracking? I don't think the cache is causing the problem because the stableStringify is quick and I used very small key size (auto incrementing ids) and very small data size of a boolean. In the example I tested we would be tracking 5000 dependencies for a single iteration, while the quick alternative I proposed doesn't track dependencies (and by extension won't update automatically if one of the base atoms change), not sure how I feel about that drawback. |
The issue I had here might be related #900, I am wondering if there is any known reason behind the extremely poor performance selector + for loop. |
Is there any update on this thread? Do we know what might be the reason behind the lack of performance on the for loop? |
There were some additional performance optimizations released with 0.2, I wonder if those may have helped this issue? |
Unfortunately it didn't.
Which makes the atomFamily and selectorFamily feel useless (and I don't really like it since I like the family logic). I will try to figure out the root problem of this. |
why do you still need selectorFamily in this case? |
Of course there is no reason to do in such case - hence the quote in your question has |
I'm probably not going to code it up, but a selector might be used to
aggregate the total cost of items in an order. In that case you would have
an atom of order, an atom family of products, and a selector for the
totals. If the number of products ordered gets high that's where the system
breaks down. Not sure if this is a really realistic example though.
…On Thu, May 6, 2021 at 4:57 PM Johnny Klironomos ***@***.***> wrote:
No reason, since this is just a simplified example demonstrating the
problem.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#914 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABUHIH7M5APKNOYGYFD3MODTMMNEZANCNFSM4YRIK2UA>
.
--
Thanks, Nate!
|
After some digging I found that the |
Interesting insight! (cc @davidmccabe) |
Do we have any ideas on that? |
Is there any updates on this one? |
I found even selector is memorized, the selectorFamily access is 10x slower than normal dict attribute access |
exactly as you say |
maybe we can opt not merge every times, and give a manual flush function merge the depsMap |
Or wrap the selector.options.get, after inner function finished then register all deps |
Summary: The current algorithm for updating selector dependencies in the data flow graph is expensive. Currently we update the graph every time a new dependency is added, which scales very poorly. This optimization only updates the graph with all sycnhronous dependencies after the synchronous selector evaluation completes. We still have to update the graph after every asynchronous dependency is added since we need to know if we have to re-evaluate the selector if one of them is updated before the selector completely resolves. This optimization significantly helps the common case for synchronous dependencies: ``` Improvement Number of synchronous dependencies 2x 100 4x 1,000 40x 10,000 ``` facebookexperimental#914 Please refer this issue for detail Pull Request resolved: facebookexperimental#1515 Test Plan: Unit tests from D34100807 check for proper re-evaluation of updated async dependencies before async selectors resolve. Differential Revision: https://www.internalfb.com/diff/D33825247?entry_point=27 Pulled By: drarmstr fbshipit-source-id: 6df53e7bca2a2284c350149c662d7a7e7e31ca52
Summary: The current algorithm for updating selector dependencies in the data flow graph is expensive. Currently we update the graph every time a new dependency is added, which scales very poorly. This optimization only updates the graph with all sycnhronous dependencies after the synchronous selector evaluation completes. We still have to update the graph after every asynchronous dependency is added since we need to know if we have to re-evaluate the selector if one of them is updated before the selector completely resolves. This optimization significantly helps the common case for synchronous dependencies: ``` Improvement Number of synchronous dependencies 2x 100 4x 1,000 40x 10,000 ``` (Note: to scale to 10,000+ also requires D34633750) #914 Please refer this issue for detail Pull Request resolved: #1515 Test Plan: Unit tests from D34100807 check for proper re-evaluation of updated async dependencies before async selectors resolve. Test performance of scalability with `Recoil_Perf-test.js` Reviewed By: mondaychen Differential Revision: D33825247 Pulled By: drarmstr fbshipit-source-id: 943b64ef371833c1f77c0185c3c13d239526ad39
Summary: The current algorithm for updating selector dependencies in the data flow graph is expensive. Currently we update the graph every time a new dependency is added, which scales very poorly. This optimization only updates the graph with all sycnhronous dependencies after the synchronous selector evaluation completes. We still have to update the graph after every asynchronous dependency is added since we need to know if we have to re-evaluate the selector if one of them is updated before the selector completely resolves. This optimization significantly helps the common case for synchronous dependencies: ``` Improvement Number of synchronous dependencies 2x 100 4x 1,000 40x 10,000 ``` (Note: to scale to 10,000+ also requires D34633750) facebookexperimental/Recoil#914 Please refer this issue for detail Pull Request resolved: facebookexperimental/Recoil#1515 Test Plan: Unit tests from D34100807 check for proper re-evaluation of updated async dependencies before async selectors resolve. Test performance of scalability with `Recoil_Perf-test.js` Reviewed By: mondaychen Differential Revision: D33825247 Pulled By: drarmstr fbshipit-source-id: 943b64ef371833c1f77c0185c3c13d239526ad39
Summary: The current algorithm for updating selector dependencies in the data flow graph is expensive. Currently we update the graph every time a new dependency is added, which scales very poorly. This optimization only updates the graph with all sycnhronous dependencies after the synchronous selector evaluation completes. We still have to update the graph after every asynchronous dependency is added since we need to know if we have to re-evaluate the selector if one of them is updated before the selector completely resolves. This optimization significantly helps the common case for synchronous dependencies: ``` Improvement Number of synchronous dependencies 2x 100 4x 1,000 40x 10,000 ``` (Note: to scale to 10,000+ also requires D34633750) facebookexperimental/Recoil#914 Please refer this issue for detail Pull Request resolved: facebookexperimental/Recoil#1515 Test Plan: Unit tests from D34100807 check for proper re-evaluation of updated async dependencies before async selectors resolve. Test performance of scalability with `Recoil_Perf-test.js` Reviewed By: mondaychen Differential Revision: D33825247 Pulled By: drarmstr fbshipit-source-id: 943b64ef371833c1f77c0185c3c13d239526ad39
Summary: The current algorithm for updating selector dependencies in the data flow graph is expensive. Currently we update the graph every time a new dependency is added, which scales very poorly. This optimization only updates the graph with all sycnhronous dependencies after the synchronous selector evaluation completes. We still have to update the graph after every asynchronous dependency is added since we need to know if we have to re-evaluate the selector if one of them is updated before the selector completely resolves. This optimization significantly helps the common case for synchronous dependencies: ``` Improvement Number of synchronous dependencies 2x 100 4x 1,000 40x 10,000 ``` (Note: to scale to 10,000+ also requires D34633750) facebookexperimental/Recoil#914 Please refer this issue for detail Pull Request resolved: facebookexperimental/Recoil#1515 Test Plan: Unit tests from D34100807 check for proper re-evaluation of updated async dependencies before async selectors resolve. Test performance of scalability with `Recoil_Perf-test.js` Reviewed By: mondaychen Differential Revision: D33825247 Pulled By: drarmstr fbshipit-source-id: 943b64ef371833c1f77c0185c3c13d239526ad39
Hello I'm experiencing a weird performance issue regarding atomFamily and selectorFamily when used with big collections.
Here is my code:
The idea is that we fetch and keep a lot of resources inside this atom
resourcesByIdAtom
, and we are using atomFamily in order to be able to access them also directly, based on their id.But, there is a case we need to pass a lot of ids directly and read the data for all of them.
So using
useResourcesValue
with a big array of ids, it takes a lot of time to calculate, cpu is on 100% and the main thread is completely bloated.Is there any suggestions? Am I doing something completely wrong?
I'd expect selectors, especially for something that haven't change to be super fast.
The text was updated successfully, but these errors were encountered: