-
Notifications
You must be signed in to change notification settings - Fork 4
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
Scanning all Objective-C classes on +(void)load
slows down app startup time
#10
Comments
+(void)load
slows down app startup time+(void)load
slows down app startup time
Hi! Thanks for issue reporting! Have you observed the problem on v1.1.0? Because I've moved the registering code from |
Hi @ikhvorost , thanks for the quick reply! I forgot the exact library version but we noticed the slowdown for the version with FYI we just decided to use something like the following: // Define this
private static let reactNativeRegisterModulesOnce: Void = {
RCTRegisterModule(MyRnModuleABC.self)
RCTRegisterModule(MyRnModuleXYZ.self)
}()
// And call the following just before RCTBridge initialization
_ = reactNativeRegisterModulesOnce In this way module registration does not have additional overhead of iterating through all the Objective-C classes. The drawback is we need to maintain this list manually. For our use case this is totally fine but I think this is not good as a library. I was thinking to abstract away this in Macro but maybe it's hard? It looks like there is no way to call something "very early" reliably in Swift. Lazy initialization (e.g. |
Hi @naruaway! The registering code takes about 15 ms with 34673 classes from my tests on a real iPhone. Don't know why but your code maybe have many classes with many methods so that they consume much time to process. Anyway I'm considering optimising auto registration for the react module classes. |
Thanks @ikhvorost! Actually for our case it was around 50000 classes so 15ms is much faster than what I measured. Also note that the inner loop count was around 90000. And for my case, it was around 500ms. I am curious, how did you measure that? I ran BTW I observed the original issue for my work and the app is not open source. Also for this work we are fine with |
I just use for my code: NSDate *date = NSDate.date;
// Hard work
...
NSLog(@"Timeout: %f", -date.timeIntervalSinceNow); |
@ikhvorost thanks, I used When I deleted the app and launch it for the first time, it's around 300ms so I think the duration changes drastically depending on the "coldness" of the app. Anyway even 20ms - 40ms is kind of too much just for registering the classes. Just note that for our app, we noticed the slowdown via Xcode Organizer and it was around 500ms for the 50th percentile. It should be also because our app is definitely larger than "empty app". So I think this slowdown is noticeable for real users for real world big enough apps |
@ikhvorost I created a reproducible script for the benchmark with xctrace: https://github.com/naruaway-sandbox/2024-07-ios-iterate-all-objc-classes-perf TL;DR; For my iPhone 13 mini with minimal "hello world" app, "from app start to static initialization" increased 37 ms, which is 40% increase and this is significant. I guess this slowdown might be non-linear for the app complexity increase. (I put the above image just for a quick reference: these images with more details are in https://github.com/naruaway-sandbox/2024-07-ios-iterate-all-objc-classes-perf) |
Hi @naruaway! I've tested ReactBridge on my huge real project and got the same result: 53508 classes and ~60 ms registering delay. The root cause of the issue is running code on the main thread and it blocks loading of an app. So I've moved execution of the code to the background thread and now it doesn't block the main one anymore. You can try the changes with new version 1.2.1. |
Thanks! Now I think in the real world with real devices, which can include legacy devices, the slowdown can be higher than experiments with our own (stable and new?) devices Oh the solution looks interesting! But is it really 100% safe to call these Objective-C related functions in a background thread? I think for now we would keep using our own way (#10 (comment)) for simplicity / predictability 🙏 . Actually I think in some edge cases registration might not complete when the first RCTBridge gets loaded in this way. Comparing with-init and without-init in this trace, I think that issue could really happen. Hmm it's perfect if there is a way to generate C / ObjC from Swift macro to automatically derive
|
First, the registering code under But you are right the best case is calling it for a particular class directly (like ObjC macros do) on an app startup and now I'm probing other future approaches like |
Thanks for clarification! Good to know it's thread-safe 🙏
I think my main concern is this. In our app, React Native is just a small piece of experiment and we do not want to take any risk to affect the app itself even for very rare edge case. I think there is no guarantee for the timing when the bridge creation finishes.
Cool! Now thinking it might be reasonable to provide "manual registration mode" as well (people can vendor & patch like we did though) especially for users who do not need React Native on start up (with background thread it should not matter much for this case but there are various types of apps; maybe some apps have already optimized things a lot and they might be saturating all the CPU cores on start up.. ) BTW I was browsing through Expo related things on the internet and just found ReactBridge is mentioned in https://github.com/EvanBacon/more-reacty-native-demo/blob/main/README.md#other-cool-stuff 🚀 by Evan Bacon! And coincidentally, "Indeed Job Search" app is what I was helping the performance issue (not sure how he found and how he reverse-engineered our app). Our app is legacy & big app and React Native is only used for small part of UI as an experiment. ReactBridge is so nice since we can just use clean Swift! Thank you so much for this library ❤️ |
Hi, thanks for this nice library! We found one performance issue and we fixed it by ourselves with some workaround but I'll leave the description here.
When using ReactBridge, the app invokes this function on startup and when the app is big enough, this can take more than 500ms. In our case, the number of Objective-C classes was more than 50000. Since most of Objective-C classes are not relevant for ReactBridge, this is inefficient. I am curious whether we have alternative way to call
_registerModule
without scanning all the Objective-C classes and methods.The text was updated successfully, but these errors were encountered: