-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
perf(Android): getConstants() in thread to parallelize load, speed up for most #680
perf(Android): getConstants() in thread to parallelize load, speed up for most #680
Conversation
What are your measurements (before vs after) and how did you measure time-to-load? |
This was measured by adding a Below are 3 measurements from before and after the patch, each starting from a cold environment (app was killed). As you can see, the GET_CONSTANTS duration gets reduced to almost nothing. BEFORE:
AFTER:
Your mileage may vary, since this depends on how much time is in between the tl;dr: if you have a decent amount of RN modules being loaded in your app (and your device has multiple cores), this should have a positive effect. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice workaround! even with a move to a Promise style as desired in the next major rev (was planned for v2 but we had a breaking change prior, so v3) this might have value as a "constants warmer". I can't imagine it is common, but it is possible that converting this getConstants() time from serial to parallel may negatively impact people. I don't mind making this the default behavior, but how hard would it be to make two constructors, one that takes no arguments and is default to "warm constants" and one that allows a boolean and let's people defer the cost to first getConstants() invocation like existing behavior?
If it is too difficult I'd scrap the idea, but I think it might be easy assuming getConstants() will always fill lazily if the AsyncTask hasn't been called yet
That way for the few that may have taken great pains to avoid this cost in some other way, we won't be blowing up whatever work they've done
What do you think?
just marking it as "needs-changes" because that's a tag I review periodically even when PRs go to sleep |
2f1d4c4
to
55dc03d
Compare
@mikehardy the behaviour is optional now and disabled by default |
How did I miss that? Let me re-read, because that sounds perfect... |
Oh you just did it haha, thought I was losing my mind. This does sound perfect now, I will re-read and probably merge Thanks for the contribution! |
this(reactContext, false); | ||
} | ||
|
||
public RNDeviceModule(ReactApplicationContext reactContext, boolean loadConstantsAsynchronously) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes it optional, but now for this method to be invoke with async load, does it need some corresponding hook in RNDeviceInfo.java? Like RNDeviceInfo needs two constructors with a similar flag, it stores the state, then when RNDeviceInfo.createNativeModules is called it sends the flag through?
Otherwise I'm not sure how the new async style is invoked since the default (and only currently available) style from linking is to just new RNDeviceInfo and there are no other control flow entrypoints to toggle the behavior https://github.com/mcuelenaere/react-native-device-info/blob/55dc03dc46/example/android/app/src/main/java/com/example/MainApplication.java#L27
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to complicate things for what is probably a non-issue statistically but I still think it's quickly achievable :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you're right. It's because locally I'm using Turbo Modules and thus creating the RNDeviceModule
myself. I'll adapt RNDeviceInfo
to also take a constructor argument.
I pulled this locally and have it ready for testing - I think it needs one more tweak after I requested it be optional then it's probably good to go |
…time RNDeviceModule.getConstants() can take a lot of time (~300ms on a Galaxy S7), so try to run it as soon as possible in the background so that by the time it gets called, the constants have already been calculated and we can just return the map.
55dc03d
to
b0f7500
Compare
@mikehardy |
I'll repull and retest - super-speedy thanks! |
@mikehardy @mcuelenaere First of all thanks a lot to both of you for the fix. I would just like to point out that the change log and the README should reflect these changes. I'm still unclear on whether I should use true or false in the constructor. |
@ItsNoHax the release notes do mention it? https://github.com/react-native-community/react-native-device-info/blob/master/CHANGELOG.md#210 (they say use I suppose the README for install could be updated, it might help to explain that the constructor even has an optional boolean argument, and what it means. Happy to take a PR for that - you can edit it directly using the GitHub UI and propose some words that you think would help people? |
@mikehardy Done, let me know what you think: #697 I left it under the main installation header as I was afraid people might miss it when linking automatically. |
Hi guys, how will this work with RN>=0.60 auto loader? |
Is there a way to make this work with autolinking or do we need to manually link to use this feature? Thank you |
@jslok to be honest I am not sure - but I am curious myself as I am just migrating my work project to RN60 now. My current understanding is that to initialize a module with a non-default constructor (specifically: one that takes an argument) in the auto-linking world, you have to use a react-native-config.js to disable autolinking, then manually link it on the platform you disabled. @thymikee is that correct? auto-linking only works with default constructors? I initially merged this patch as a new option and did not set the deferred method to default but assuming my understanding of auto-linking is correct and it will only work with a default constructor, I'd accept a patch that reversed which style was default now that it has been released a while and shown to be working. |
@mikehardy disabling linking in Passing a custom constructor is possible through module.exports = {
dependencies: {
'react-native-device-info': {
platforms: {
android: {
packageInstance: 'new RNDeviceInfo(true)'
}
}
}
}
}; |
@thymikee I really appreciate both your responsiveness, and your spot-on response, thank you! I promise I'll be good at auto-linking soon :-) Okay then, @jslok I would happily take either a PR that made the parallel version default + a quick doc snippet on how to disable it (with the snippet above I think but reversed to false) or a doc enhancement that had this snippet showing how to enable it. I think it's ready for default though, I believe more will benefit from it's being enabled than will be harmed so I would prefer just enabling by default |
I don't have a file named react-native.config.js. It is unclear from the readme whether I should create it myself and where. I'm using isTablet() method to calculate UI elemens in the first screen. So I need to load it synchronically. |
@Dror-Bar if you wanted to propose a quick readme PR that made it clear to you that would help. Specifically addressed at the confusion: the file does not exist in the repository, but if you do not want the parallel initialization, you will need to create the file, and it should contain the stanza prescribed in the react-native-config.js in the readme If you are okay with the new default / parallel initialization is okay for your project, you do not need to do anything My decision to merge this and alter the default was based on the assumption it was reasonably tested (so would not break anyone) and that in general it was a performance increase for everyone or at least did not harm things. If this is not true for your project I would love to know why in order to see if my assumption was correct - and I'd apologize for regressing your project also, that definitely wasn't intended |
Description
RNDeviceModule.getConstants() can take a lot of time (~300ms on a Galaxy S7), so try to run it as soon as possible in the background so that by the time it gets called, the constants have already been calculated and we can just return the map.
Checklist
README.md
CHANGELOG.md