-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Access Reanimated runtime only from UI thread #6770
base: main
Are you sure you want to change the base?
Conversation
Hello @kmagiera ! |
packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp
Outdated
Show resolved
Hide resolved
…odules/ReanimatedModuleProxy.cpp Co-authored-by: Marc Rousavy <[email protected]>
@kmagiera Can we please have a look at this ? really need this improvement, thank you! |
Hi @Szymon20000! I don't think this is a good solution, as this could block the JS thread even more, whenever there is a long operation on the UI thread. Also running our runtime on a separate thread, would resolve the caching problem, but would also come with a new set of problems. For example when we are running an animation we want to synchronously update the UI from our hermes runtime. Putting it on a separate thread, would mean that we have to schedule these updates to the UI thread, causing additional delays. I think the best solution would be per thread caching in hermes. Our current approach is optimized for the most common use case for reanimated and before this native stack check was introduced it had the best performance. If we can get this change in Hermes, then I think there's no need to change it in reanimated. If you need a temporary workaround I think you could try using the fact that this native stack check is behind an ifdef. So you could change it back to the old mechanism. |
Hello @bartlomiejbloniarz ! |
I've tried this patch and here are the results. Mounting of an expensive and reanimated heavy component got much faster on a slow android device. The image is loading from disk cache so it flickers there. Before: PXL_20241128_111359338.mp4After: PXL_20241128_111538693.mp4 |
I understand that it can give a performance boost in some apps, but it can also result in performance degradation in other apps. I think that this is a huge behavior change in reanimated, that could result in new performance issues, just for other use cases. I'm not sure if this change would be a net positive for our users. I would have to first test this to ensure that we are not breaking anything. I think that if it works for you, you should for now just patch-package it in reanimated (or disable the checking in Hermes). If we have more data that show that this is overall a better solution, we can merge this change. |
Mounting reanimated |
I scanned the attached issue and it seem like addressing this on hermes side is ultimately the way to go. This does not rule out improvements that we could make on reanimated side to possible mitigate this, but what is not clear from the report is:
As for this particular change one thing I'd be concerned the most is whether it may result in deadlocks. From concurrency perspective, locking on VM would never cause that while scheduling and waiting could. Second concerns is the granularity of tasks which is more coarse grained compared to blocks that require exclusive access to the runtime (there could be a single task that runs hundreds of such blocks, allowing resource locking approach to interleave the task in between those calls and as a consequence to simply wait shorter) Finally, the main reason for introducing sync JS calls was to provide a way of accessing values synchronously while avoiding preemptive serialization of every single value that exists in the system. This was a huge performance boost but it relied on the sync value access to be of a relatively rare occurrence. Regardless of whether they rely on resource locking or scheduling as proposed here, they'd still block the execution in an nondeterministic way depending on how much stuff is happening. In the examples you posted I wonder, maybe this mechanism is simply misused, perhaps, even by the core reanimated code, and some sync JS calls can be simply avoided, similarily to how cancelAnimation calls relied on it and got fixed in #6564 |
The main problem of the mounting time currently is the fact that both updater is the worklet you pass as callback to each hook, so it's called to calcualte the initial state: react-native-reanimated/packages/react-native-reanimated/src/hook/useDerivedValue.ts Line 58 in 0162804
react-native-reanimated/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts Line 454 in 0162804
And react-native-reanimated/packages/react-native-reanimated/src/mutables.ts Lines 151 to 157 in 0162804
This is basically what i see in my recordings with that gallery component which has lots of shared values and derived values. |
I see. Perhaps one of those ideas would make sense:
|
Spent a bit more time testing this patch and it definitely introduced a few bugs. For example |
+1 that this is a significant problem and needs to either be solved or prominently called out in the docs. We're currently trying to protect against this with |
Looks like the fix in Hermes landed already: facebook/hermes#1572 (comment) Still the suggestions from my previous comment may still make sense as further optimisations. |
Currently synchronous calls from js thread only waits for lock but still access UI runtime from js thread. That causes hermes to go for slow path as it only remembers last cached thread. More info here facebook/hermes#1572
Improvement:
Downside:
Possible alternatives:
Summary
Test plan