diff options
author | Sam Berlin <sameb@google.com> | 2014-05-10 10:34:16 -0400 |
---|---|---|
committer | Sam Berlin <sameb@google.com> | 2014-05-10 10:34:16 -0400 |
commit | 409e0f578b620c38f6c8626dee78783219d2956e (patch) | |
tree | b884da0ec0f1c4566558e6da54b9a09188944fa4 /core/src/com/google/inject | |
parent | 4daa205dc4fe1d339f91155dde65f3941cbd144c (diff) | |
download | guice-409e0f578b620c38f6c8626dee78783219d2956e.tar.gz |
Try to use cglibs FastClass to invoke @Provides methods, it's faster!
Here is a caliper benchmark to demonstrate the difference.
https://caliper.googleplex.com/runs/2d349fec-2742-45e1-b6e5-16997c522387#r:scenario.benchmarkSpec.methodName,scenario.benchmarkSpec.parameters.strategy
This should save about 200-300 ns per method invocation and about 224 bytes (over 4 objects) of allocations for each invocation.
The cost of this (of course) is greater permgen usage and potentially slower startup/injector creation due to the class generation.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=66364901
Diffstat (limited to 'core/src/com/google/inject')
-rw-r--r-- | core/src/com/google/inject/internal/ProviderMethod.java | 109 | ||||
-rw-r--r-- | core/src/com/google/inject/internal/ProviderMethodsModule.java | 2 |
2 files changed, 102 insertions, 9 deletions
diff --git a/core/src/com/google/inject/internal/ProviderMethod.java b/core/src/com/google/inject/internal/ProviderMethod.java index d8bc18b1..8746ae10 100644 --- a/core/src/com/google/inject/internal/ProviderMethod.java +++ b/core/src/com/google/inject/internal/ProviderMethod.java @@ -17,12 +17,14 @@ package com.google.inject.internal; import com.google.common.base.Objects; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.inject.Binder; import com.google.inject.Exposed; import com.google.inject.Key; import com.google.inject.PrivateBinder; import com.google.inject.Provider; +import com.google.inject.internal.BytecodeGen.Visibility; import com.google.inject.internal.util.StackTraceElements; import com.google.inject.spi.BindingTargetVisitor; import com.google.inject.spi.Dependency; @@ -35,6 +37,7 @@ import com.google.inject.spi.ProvidesMethodTargetVisitor; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.List; import java.util.Set; @@ -43,12 +46,44 @@ import java.util.Set; * * @author jessewilson@google.com (Jesse Wilson) */ -public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies, +public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies, ProvidesMethodBinding<T> { + + /** + * Creates a {@link ProviderMethod}. + * + * <p>Ideally, we will use {@link FastClass} to invoke the actual method, since it is + * significantly faster. However, this will fail if the method is {@code private} or + * {@code protected}, since fastclass is subject to java access policies. + */ + static <T> ProviderMethod<T> create(Key<T> key, Method method, Object instance, + ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders, + Class<? extends Annotation> scopeAnnotation) { + int modifiers = method.getModifiers(); + /*if[AOP]*/ + if (!Modifier.isPrivate(modifiers) && !Modifier.isProtected(modifiers)) { + try { + // We use an index instead of FastMethod to save a stack frame. + return new FastClassProviderMethod<T>( + key, method, instance, dependencies, parameterProviders, scopeAnnotation); + } catch (net.sf.cglib.core.CodeGenerationException e) {/* fall-through */} + } + /*end[AOP]*/ + + if (!Modifier.isPublic(modifiers) || + !Modifier.isPublic(method.getDeclaringClass().getModifiers())) { + method.setAccessible(true); + } + + return new ReflectionProviderMethod<T>( + key, method, instance, dependencies, parameterProviders, scopeAnnotation); + } + + protected final Object instance; + protected final Method method; + private final Key<T> key; private final Class<? extends Annotation> scopeAnnotation; - private final Object instance; - private final Method method; private final ImmutableSet<Dependency<?>> dependencies; private final List<Provider<?>> parameterProviders; private final boolean exposed; @@ -56,7 +91,7 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe /** * @param method the method to invoke. It's return type must be the same type as {@code key}. */ - ProviderMethod(Key<T> key, Method method, Object instance, + private ProviderMethod(Key<T> key, Method method, Object instance, ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders, Class<? extends Annotation> scopeAnnotation) { this.key = key; @@ -66,8 +101,6 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe this.method = method; this.parameterProviders = parameterProviders; this.exposed = method.isAnnotationPresent(Exposed.class); - - method.setAccessible(true); } public Key<T> getKey() { @@ -110,9 +143,8 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe } try { - // We know this cast is safe becase T is the method's return type. @SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" }) - T result = (T) method.invoke(instance, parameters); + T result = (T) doProvision(parameters); return result; } catch (IllegalAccessException e) { throw new AssertionError(e); @@ -121,6 +153,10 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe } } + /** Extension point for our subclasses to implement the provisioning strategy. */ + abstract Object doProvision(Object[] parameters) + throws IllegalAccessException, InvocationTargetException; + public Set<Dependency<?>> getDependencies() { return dependencies; } @@ -156,4 +192,61 @@ public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDe // (We need to call equals, so we do. But we can avoid hashCode.) return Objects.hashCode(method); } + + /*if[AOP]*/ + /** + * A {@link ProviderMethod} implementation that uses {@link net.sf.cglib.reflect.FastClass#invoke} + * to invoke the provider method. + */ + private static final class FastClassProviderMethod<T> extends ProviderMethod<T> { + final net.sf.cglib.reflect.FastClass fastClass; + final int methodIndex; + + FastClassProviderMethod(Key<T> key, + Method method, + Object instance, + ImmutableSet<Dependency<?>> dependencies, + List<Provider<?>> parameterProviders, + Class<? extends Annotation> scopeAnnotation) { + super(key, method, instance, dependencies, parameterProviders, scopeAnnotation); + // We need to generate a FastClass for the method's class, not the object's class. + this.fastClass = + BytecodeGen.newFastClass(method.getDeclaringClass(), Visibility.forMember(method)); + // Use the Signature overload of getIndex because it properly uses return types to identify + // particular methods. This is normally irrelevant, except in the case of covariant overrides + // which java implements with a compiler generated bridge method to implement the override. + this.methodIndex = fastClass.getIndex( + new net.sf.cglib.core.Signature( + method.getName(), org.objectweb.asm.Type.getMethodDescriptor(method))); + Preconditions.checkArgument(this.methodIndex >= 0, + "Could not find method %s in fast class for class %s", + method, + method.getDeclaringClass()); + } + + @Override public Object doProvision(Object[] parameters) + throws IllegalAccessException, InvocationTargetException { + return fastClass.invoke(methodIndex, instance, parameters); + } + } + /*end[AOP]*/ + + /** + * A {@link ProviderMethod} implementation that invokes the method using normal java reflection. + */ + private static final class ReflectionProviderMethod<T> extends ProviderMethod<T> { + ReflectionProviderMethod(Key<T> key, + Method method, + Object instance, + ImmutableSet<Dependency<?>> dependencies, + List<Provider<?>> parameterProviders, + Class<? extends Annotation> scopeAnnotation) { + super(key, method, instance, dependencies, parameterProviders, scopeAnnotation); + } + + @Override Object doProvision(Object[] parameters) throws IllegalAccessException, + InvocationTargetException { + return method.invoke(instance, parameters); + } + } } diff --git a/core/src/com/google/inject/internal/ProviderMethodsModule.java b/core/src/com/google/inject/internal/ProviderMethodsModule.java index 7ec3075a..062653d2 100644 --- a/core/src/com/google/inject/internal/ProviderMethodsModule.java +++ b/core/src/com/google/inject/internal/ProviderMethodsModule.java @@ -123,7 +123,7 @@ public final class ProviderMethodsModule implements Module { binder.addError(message); } - return new ProviderMethod<T>(key, method, delegate, ImmutableSet.copyOf(dependencies), + return ProviderMethod.create(key, method, delegate, ImmutableSet.copyOf(dependencies), parameterProviders, scopeAnnotation); } |