views:

780

answers:

5

I am wondering if there are any additional optimizations I can implement to improve the speed of reflective invocations in Java. Not that the performance is prohibitive, but I get the willies when thinking about some piece of code in a library I am writing being implemented in a tight loop somewhere.

Consider a utility method to invoke reflectively:

public static Object invoke(Object targetObject, String methodName, Object[] arguments, Class<?>[] signature)

The basic operation is

return method.invoke(targetObject, arguments);

As a performance optimization, I cache the method using a hash of the target object's class, method name and signature (the code of which might use some improvement) but beyond that, is there anything else I can do ? I have heard references to some early implementations of InvokeDynamic that sound promising, but I just assumed that they were probably not applicable yet, and I discounted my own byte code manipulation as I would like to keep the utility simple (but fast).

Cheers.

+4  A: 

The comments below relate to Sun's implementation, in particular OpenJDK 6. Your mileage may vary with other Java platform implementations.

java.lang.Class does some caching itself, so implementing your own cache may not improve things very much. Do timing tests with and without manual caching.

The actual invocation mechanism is optimised too. The first 15 runs (by default) of your reflected method is called using JNI; after that, bytecode is generated and calling that reflected method would perform identically to calling that method directly in Java code.

Chris Jester-Young
Wow I never knew that. I will do those timing tests.Is there any way to override the 15 default ?Any other way to persuade the compiler to kick in early ?
Nicholas
Just set the sun.reflect.inflationThreshold property to the number you prefer.
Chris Jester-Young
This is amazing info. Thanks.
Yoni Roit
A: 

Premature Optimization is generally bad. No matter what, your performance will still be many times that of a dynamic language, so I really wouldn't worry about it beyond documenting the fact that it uses reflection and therefore might be sub-optimal.

Also, there is a good chance that either now or in the future, Java will optimize the bytecode so that the call doesn't cost anything more than a method call when used in a loop. Your "Optimization" may actually hinder the compilers ability to do something like that. (I know I'm being vague, but this has happened--a LOT).

Bill K
Conceded, but in the process of being a bit sneaky, I found that non-public methods require climbing the inheritance tree to find methods in parents so I found some cases required calling get[Declared]Method several times. Also, I only have to call setAccessible(true) once. Am I still off base ?
Nicholas
I don't think you're off base, it's an interesting analysis, but unless you find a necessity, you should just about always use the most readable, simple and direct solution you can come up with.
Bill K
A: 

You will definitely want to reflect the method object only once (likely as a private static), and use that for the invocation rather than reflect it out every time. Don't bother with a cache map unless you don't know the name at compile time.

If it is sensible in your context, you may want to hang on to and reuse the argument array object (don't forget to null the array elements on method exit to prevent temporary GC inhibition).

If always invoked with the same parms (highly unlikely), you could hang onto the parms and (the argument array with it's values) reuse them.

I would not attempt anything beyond that for the reasons already given by the other answers.

Software Monkey
A: 

I ran some tests around Chris Jester-Young's answer and using the verbose options, I definitely observed the compiler take some action around the 15th invocation. Hard to say if there's much performance differential without a more sophisticated test, but it is persuasive. Here's the output:

Test# 0
Test# 1
Test# 2
Test# 3
Test# 4
Test# 5
Test# 6
Test# 7
Test# 8
Test# 9
Test# 10
Test# 11
Test# 12
Test# 13
Test# 14
[Loaded sun.reflect.ClassFileConstants from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.AccessorGenerator from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.MethodAccessorGenerator from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ByteVectorFactory from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ByteVector from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ByteVectorImpl from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ClassFileAssembler from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.UTF8 from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded java.lang.Void from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.Label from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.Label$PatchInfo from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded java.util.AbstractList$Itr from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.MethodAccessorGenerator$1 from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ClassDefiner from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ClassDefiner$1 from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]
Test# 15
Test# 16
Test# 17

I guess the InvokeDynamic business is not attracting too many developers on the basis of reflection speedup/elimination.

Thanks Chris.

Nicholas
A: 

If the cost of invocation is less than 10% of the cost of what goes on in the method, it's hardly worth worrying about.

You can determine that by running it 10^6 times in a loop, with and without the guts of the routine. Time it with a stopwatch, so seconds translate to microseconds.

Mike Dunlavey