Java Reflection Performance Error

I know that there are many topics talking about Reflection performance.

Even the official Java docs say Reflection is slower, but I have this code:

public class ReflectionTest { public static void main(String[] args) throws Exception { Object object = new Object(); Class<Object> c = Object.class; int loops = 100000; long start = System.currentTimeMillis(); Object s; for (int i = 0; i < loops; i++) { s = object.toString(); System.out.println(s); } long regularCalls = System.currentTimeMillis() - start; java.lang.reflect.Method method = c.getMethod("toString"); start = System.currentTimeMillis(); for (int i = 0; i < loops; i++) { s = method.invoke(object); System.out.println(s); } long reflectiveCalls = System.currentTimeMillis() - start; start = System.currentTimeMillis(); for (int i = 0; i < loops; i++) { method = c.getMethod("toString"); s = method.invoke(object); System.out.println(s); } long reflectiveLookup = System.currentTimeMillis() - start; System.out.println(loops + " regular method calls:" + regularCalls + " milliseconds."); System.out.println(loops + " reflective method calls without lookup:" + reflectiveCalls+ " milliseconds."); System.out.println(loops + " reflective method calls with lookup:" + reflectiveLookup + " milliseconds."); } 

}

What I don't think is a valid benchmark, but at least should show some difference. I completed it, expecting that normal reflection calls would be slightly slower than normal.

But it prints this:

 100000 regular method calls:1129 milliseconds. 100000 reflective method calls without lookup:910 milliseconds. 100000 reflective method calls with lookup:994 milliseconds. 

Just for the record, I first performed it without this sysouts group, and then realized that some JVM optimizations just made it work faster, so I added this data to see if the reflection was even faster.

Result without sysouts:

 100000 regular method calls:68 milliseconds. 100000 reflective method calls without lookup:48 milliseconds. 100000 reflective method calls with lookup:168 milliseconds. 

I saw over the Internet that the same test performed on old JVMs makes reflective searchless twice as slow as regular calls, and this speed drops on new updates. If someone can do this and tell me that I'm wrong, or at least show me if there is something different from the past that accelerates its implementation.

Following the instructions, I ran each loop, divided, and the result (without sysouts)

 100000 regular method calls:70 milliseconds. 100000 reflective method calls without lookup:120 milliseconds. 100000 reflective method calls with lookup:129 milliseconds. 
+6
source share
8 answers

Never perform performance checks on different bits of code in the same “run”. The JVM has various optimizations that mean it, although the end result is the same as the internal functions performed, may vary. More specifically, during the test, the JVM may have noticed that you are calling Object.toString a lot and have begun to inline calls to the Object.toString method. Perhaps he began to carry out a deployment cycle. Or there may be garbage collection in the first loop, but not the second or third loop.

To get a more meaningful, but still not entirely accurate picture, you should divide your test into three separate programs.

Results on my computer (no print and 1,000,000 runs each)

All three cycles are performed in one program.

1,000,000 regular method calls: 490 milliseconds.

1,000,000 reflexive method calls without searching: 393 milliseconds.

1,000,000 reflexive method calls with a loop: 978 milliseconds.

Cycles run in separate programs.

1,000,000 regular method calls: 475 milliseconds.

1,000,000 reflexive method calls without searching: 555 milliseconds.

1,000,000 reflexive method calls with a loop: 1,160 milliseconds.

+11
source

Here's an article by Brian Goetz on microfunkets that you should read. It seems that you are not doing anything to warm up the JVM (which makes it possible to do everything possible to make any inlays or other optimizations) before taking measurements, therefore, probably, the non-reflective test still does not heat up - but this can distort your numbers.

+4
source

Micro benchmarks like this one will never be accurate at all - as the VM “warms up”, it will embed code bits and optimize code bits as it moves, so the same thing was done after 2 minutes, the program can significantly exceed it at the beginning.

In terms of what happens here, I assume that the first “normal” block of a method call heats it, so reflective blocks (and even all subsequent calls) will be faster. The only overhead caused by the reflexive call of the method that I see is finding a pointer to this method, which in any case is a nanosecond operation and will be easily cached by the JVM. The rest will be related to how the virtual machine heats up, and by the time you reach the reflective calls.

+2
source

When you have several long work cycles, the first cycle can initiate a compilation method, as a result of which later cycles will be optimized from the very beginning. However, optimization may not be optimal because it does not have run-time information for these loops. ToString is relatively expensive, and a pair takes longer than reflection calls.

You do not need separate programs to avoid loop optimization due to an earlier cycle. You can run them in many ways.

Results

 Average regular method calls:2 ns. Average reflective method calls without lookup:10 ns. Average reflective method calls with lookup:240 ns. 

The code

 import java.lang.reflect.Method; public class ReflectionTest { public static void main(String[] args) throws Exception { int loops = 1000 * 1000; Object object = new Object(); long start = System.nanoTime(); Object s; testMethodCall(object, loops); long regularCalls = System.nanoTime() - start; java.lang.reflect.Method method = Object.class.getMethod("getClass"); method.setAccessible(true); start = System.nanoTime(); testInvoke(object, loops, method); long reflectiveCalls = System.nanoTime() - start; start = System.nanoTime(); testGetMethodInvoke(object, loops); long reflectiveLookup = System.nanoTime() - start; System.out.println("Average regular method calls:" + regularCalls / loops + " ns."); System.out.println("Average reflective method calls without lookup:" + reflectiveCalls / loops + " ns."); System.out.println("Average reflective method calls with lookup:" + reflectiveLookup / loops + " ns."); } private static Object testMethodCall(Object object, int loops) { Object s = null; for (int i = 0; i < loops; i++) { s = object.getClass(); } return s; } private static Object testInvoke(Object object, int loops, Method method) throws Exception { Object s = null; for (int i = 0; i < loops; i++) { s = method.invoke(object); } return s; } private static Object testGetMethodInvoke(Object object, int loops) throws Exception { Method method; Object s = null; for (int i = 0; i < loops; i++) { method = Object.class.getMethod("getClass"); s = method.invoke(object); } return s; } } 
+2
source

There is no reason why a reflective call should be slower than a regular call. JVM can optimize them in the same thing.

In fact, human resources are limited, and first they need to optimize ordinary challenges first. Over time, they can work to optimize reflective calls; especially when reflection is becoming more popular.

+2
source

I am writing my own micro-benchmark without loops and System.nanoTime() :

 public static void main(String[] args) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { Object obj = new Object(); Class<Object> objClass = Object.class; String s; long start = System.nanoTime(); s = obj.toString(); long directInvokeEnd = System.nanoTime(); System.out.println(s); long methodLookupStart = System.nanoTime(); java.lang.reflect.Method method = objClass.getMethod("toString"); long methodLookupEnd = System.nanoTime(); s = (String) (method.invoke(obj)); long reflectInvokeEnd = System.nanoTime(); System.out.println(s); System.out.println(directInvokeEnd - start); System.out.println(methodLookupEnd - methodLookupStart); System.out.println(reflectInvokeEnd - methodLookupEnd); } 

I did this in Eclipse on my machine a dozen times, and the results changed quite a bit, but here is what I usually get:

  • hours of calling the direct method at 40-50 microseconds
  • hours of method searches for 150-200 microseconds
  • reflexive call using method clock pulses for 250-310 microseconds.

Now do not forget about the warnings on micro-objects described in Nathan's answer - there are, of course, many shortcomings in this micro-controller - and trust the documentation if they say that LOT reflection is slower than a direct call.

+1
source

It seems to me that you placed a call to "System.out.println (s)" inside the internal testing loop. Since the execution of IO is due to the fact that it is slow, it actually “absorbs” your test, and the call overhead becomes insignificant.

Try removing "println ()" and running this code this way, I'm sure you will be surprised at the result (some of the dumb calculations are necessary to avoid compilation that completely excludes calls):

 public class Experius { public static void main(String[] args) throws Exception { Experius a = new Experius(); int count = 10000000; int v = 0; long tm = System.currentTimeMillis(); for ( int i = 0; i < count; ++i ) { v = a.something(i + v); ++v; } tm = System.currentTimeMillis() - tm; System.out.println("Time: " + tm); tm = System.currentTimeMillis(); Method method = Experius.class.getMethod("something", Integer.TYPE); for ( int i = 0; i < count; ++i ) { Object o = method.invoke(a, i + v); ++v; } tm = System.currentTimeMillis() - tm; System.out.println("Time: " + tm); } public int something(int n) { return n + 5; } } 

- TR

0
source

Even if you look at the method in both cases (i.e., before the 2nd and 3rd cycles), the first search takes less time than the second search, which should have been in a different way and less than the usual method call on my machine.

However, if you use the 2nd loop with the method search and System.out.println , I get the following:

 regular call : 740 ms look up(2nd loop) : 640 ms look up ( 3rd loop) : 800 ms 

Without the System.out.println statement, I get:

 regular call : 78 ms look up (2nd) : 37 ms look up (3rd ) : 112 ms 
0
source

Source: https://habr.com/ru/post/894326/


All Articles