Skip to content
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

Injection on Android is slow because of getCanonicalName() #819

Closed
ctsimmonds opened this issue Jul 25, 2017 · 6 comments
Closed

Injection on Android is slow because of getCanonicalName() #819

ctsimmonds opened this issue Jul 25, 2017 · 6 comments

Comments

@ctsimmonds
Copy link

I'm converting an existing Android project from using RoboGuice to Dagger, and to my surprise injection of a fragment is slower with Dagger by about 5%. From method tracing, it looks like the culprit is that the Class.getCanonicalName() method can be really slow, particularly when called on inner classes.

For one injection that I profiled, AndroidSupportInjection.inject() takes 1.074s of CPU time as measured in the profiler. Of that, the two calls to getCanonicalName() for the logger call take 67ms and 164ms respectively. Then the call to DispatchingAndroidInjector.inject() takes 696ms, all of which is spent inside maybeInject(). That method spends 483ms on another call to getCanonicalName(), and then finally 209ms for the generated inject() method for my fragment's subcomponent.

This means that 66.5% of the total time for the injection was spent inside calls to getCanonicalName(). The last instance is particularly annoying, because it's only used as a parameter to checkNotNull() and the resulting string value is only used if the injection fails and is a complete waste of time on success path.

There are a couple of ways that this time could be improved:

  • Issue Turn off Logcat during Android Fragment injection #790 has already been raised to remove the logger statement, which would remove the first two calls to getCanonicalName().
  • Calls to checkNotNull() should only evaluate getCanonicalName() on Class objects if the reference being checked was actually null. All of the calls to it that pass foo.getClass().getCanonicalName() would just pass the class instance instead, and getCanonicalName() would only be called before throwing NullPointerException.
ctsimmonds added a commit to ctsimmonds/dagger that referenced this issue Jul 27, 2017
…lName() from the success path for injection on Android.
@GeorgePetri
Copy link

Any progress on this? It seems to be present still on 2.12.

@netdpb
Copy link
Member

netdpb commented Oct 13, 2017

Would replacing getCanonicalName() with getName() still be a problem? Or better yet, just use the class object.

@JakeWharton
Copy link

JakeWharton commented Oct 13, 2017 via email

@ronshapiro
Copy link

Sorry for not doing this yet - this is totally reasonable. Do we know why calling this method is so slow though? It's very surprising to me. Should we be discouraging calling it in general?

@ronshapiro
Copy link

*discouraging it beyond Dagger

@bejibx
Copy link

bejibx commented Oct 16, 2017

Could it be because Android uses .dex format for compiled classes?

ronshapiro pushed a commit that referenced this issue Oct 23, 2017
…ing normal execution.

Make Preconditions.checkNotNull call Class.getCanonicalName() for Class arguments. Otherwise, ensure it's called only when debugging or in error cases.

Fixes #819.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=172789896
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants