5

As you all know it is possible to fetch a method with Reflection and invoke it through the returned Method instance.

My question is however; once it is fetched by Reflection and I invoke the Method over and over again will the performance of the method be slower than the normal way of calling a method?

For example:

import java.lang.reflect.Method;

public class ReflectionTest {

    private static Method test;

    public ReflectionTest() throws Exception {
        test = this.getClass().getMethod("testMethod", null);
    }

    public void testMethod() {
        //execute code here
    }

    public static void main(String[] args) throws Exception {
        ReflectionTest rt = new ReflectionTest();
        for (int i = 0; i < 1000; i++) {
            rt.test.invoke(null, null);
        }

        for (int i = 0; i < 1000; i++) {
            rt.testMethod();
        }
    }
}

I am asking this because I am making an event system that, upon registering the listener it scans for annotations. The methods are put into a map and then they are executed each time an event occurs of their required parameter type. I don't know if this is performant enough for, for example a game.

Limnic
  • 1,826
  • 1
  • 20
  • 45
  • 1
    maybe not in execution, perhaps in fetching? – DnR Jan 12 '15 at 02:46
  • I believe if I watch out with the amount of fetching I do it shouldn't have too much of an impact on the performance? – Limnic Jan 12 '15 at 02:46
  • lets wait for the pro's answer :D – DnR Jan 12 '15 at 02:47
  • 1
    or you can [add benchmark](http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java) to analyze it yourself – DnR Jan 12 '15 at 02:55
  • Hmm thanks I'll look into that, but I still want an answer on this too. :) – Limnic Jan 12 '15 at 02:56
  • http://stackoverflow.com/questions/14146570/calling-a-getter-in-java-though-reflection-whats-the-fastest-way-to-repeatedly – kamoor Jan 12 '15 at 03:04
  • @kamoor My bad. I didn't find any related questions. I suppose the the question you linked was too specific for my search terms. – Limnic Jan 12 '15 at 03:06
  • @Stephen: how is this not a duplicate? – Jeroen Vannevel Jan 12 '15 at 03:13
  • 2
    Because the other question is asking about performance of different ways of doing reflection, while this asks about reflection versus pure calls. I quote: *"My question is however; once it is fetched by Reflection and I invoke the Method over and over again will the performance of the method be slower than the normal way of calling a method?"* – Stephen C Jan 12 '15 at 03:15
  • @StephenC: The accepted answer to that question nicely compares 4 different ways of calling a method through reflection *and* compares it to direct invocation. It depends on what you base the closing at, I suppose. Personally I don't see much use in just repeating that answer here in a poorer form. – Jeroen Vannevel Jan 12 '15 at 03:21
  • @JeroenVannevel I don't think people who were looking for this answer will find the related question which as you said compares different ways. This one is specific. – Limnic Jan 12 '15 at 03:24

2 Answers2

3

Using the method without reflection is about an order of magnitude faster. I tested it like

public static void main(String[] args) throws Exception {
    ReflectionTest rt = new ReflectionTest();
    // Warm up
    for (int i = 0; i < 100; i++) {
        test.invoke(rt, null);
    }
    for (int i = 0; i < 100; i++) {
        rt.testMethod();
    }

    long start = System.nanoTime();
    for (int i = 0; i < 10000; i++) {
        test.invoke(rt, null);
    }
    long end = Math.abs((start - System.nanoTime()) / 1000);
    start = System.nanoTime();
    for (int i = 0; i < 10000; i++) {
        rt.testMethod();
    }
    long end2 = Math.abs((start - System.nanoTime()) / 1000);
    System.out.printf("%d %d%n", end, end2);
}

I also moved test to a static field so it would compile and run

private static Method test;
static {
    try {
        test = ReflectionTest.class.getMethod("testMethod");
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    }
}

I get a fairly consistent difference (or an output consistent) with

4526 606

Which indicates that across 10000 invocations reflection is ~7 times slower then direct invocation.

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • Hmm. That's dissapointing :(. I really like the way my event system works. This same test took `1740 228` on my computer. Do you think I should keep the event system the way it is or do you have any suggestions on a better event system? – Limnic Jan 12 '15 at 03:10
  • Honestly, I think it's possibly a case of premature optimization. The extra flexibility that reflection gives you seems to be worth the penalty, and if there's some critical path then you can optimize that when you find it. – Elliott Frisch Jan 12 '15 at 03:12
  • @Limnic - how do you expect someone to answer that ... unless they have *seen* the code for your event system? And yes, "premature optimization"!! – Stephen C Jan 12 '15 at 03:18
  • After looking at a related question provided in the comments of my question; I used `MethodHandle` instead of `Method` which yielded better results: `850 239`. @StephenC I suppose I was just looking for an opinion wether a reflection-based event system is acceptable for applications where performance matters. – Limnic Jan 12 '15 at 03:18
  • You could also check Java 8 style `Function.apply` or `Consumer.accept` for comparative performance of the newly implemented functional capabilities – Alex Jan 12 '15 at 03:35
  • @Limnic *It's not disappointing, it's irrelevant.* A warmup of 100 iterations is far too short and execution times of a few microseconds say *absolutely nothing* about the real behavior. Moreover, benchmarking an empty method is a non-sense as it can be completely eliminated. Use a benchmarking framework, or at the very least re-run it scaled up by a factor of about one million. – maaartinus Jan 12 '15 at 06:10
2

@Elliot Frisch's answer provides conclusive1 evidence that using Method.invoke() is slower.

You would expect this anyway, because the reflective version involves extra work; e.g.

  • the creation and initialization of an array containing the varags,
  • checking the length of the array, and
  • casting the arguments in the array from Object to the respective parameter types.

It is possible that the JIT could could optimize this in some cases ...


1 - OK ... inconclusive. The benchmark is questionable because it doesn't take proper care to deal with possible JVM warmup anomalies.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216