6

I know, using Reflection API, we can call methods by their name stored in a string.

But, Reflection API cannot be used in a high performance application. In my application, methods will be invoked at very high rate. So, I cannot use Reflection API.

So, what are the alternatives for Reflection API?

I did research and found out cglib and other code generation libraries can be used.

But, I did not find any example to invoke method by its name stored in a string.

An example would also be great with the reflection alternative.

Update: Actually I am implementing some Master-Slave communication API. In which slaves will call master methods remotely. And, method invocations will be at very high rate (Approx 50 method invocation per second). As, master is continuously polling slaves for any response. So, should I give reflection a go at this high invocation rate?

user207421
  • 305,947
  • 44
  • 307
  • 483
Akshat
  • 720
  • 9
  • 24
  • Parsing and processing the String is far more expensive than using reflection. Reflection does cost, but not as much as looking up a String. What are your requirements in terms of performance? – Peter Lawrey May 10 '14 at 10:13
  • 1
    Your problem is that calling methods by choosing them with a `String` will, inherently, be pretty slow. – Louis Wasserman May 10 '14 at 11:40
  • 3
    With 50 invocations per second ANY technique will do. With reflection you'll be able to call up to 50 *million* methods per second. – apangin May 10 '14 at 22:43
  • Are you using java 7+? – Bohemian May 10 '14 at 23:43
  • Yes, I am using Java 7. – Akshat May 11 '14 at 04:50
  • @apangin But, that rate does not put much load on the system, does it? – Akshat May 11 '14 at 04:51
  • @Akshat Right, there should be no performance issues at such a rate (at least, related to method lookup or invocation). – apangin May 11 '14 at 10:38
  • In a remote method invocation scenario, you will need to worry more about network latency and I/O limits than reflection API performance, which is orders of magnitude faster than the I/O incurred in the call. We implemented RPC over XMPP with the method-caching technique described below. – maasg May 11 '14 at 13:05

5 Answers5

6

This is what reflection is for. Before ruling it out, I'd suggest giving it a try and seeing whether, on any JVM from the last several years, you actually see any performance issue related to it. I suspect you won't.

Your only other real option (actually, there's cglib; see this other answer for more, and why you may not want to use it) is a method that you let people call, pass in the name of the method to call, and then dispatch to that method (e.g., with a big switch, or a dispatch table, or similar). E.g.:

public Object callMethod(String methodName, Object[] args) {
    switch (methodName) { // Using strings in `switch` requires a recent version of Java
        case "foo":
            return this.foo(args[0]);
        case "bar":
            this.bar(args[0], args[1]);
            return null;
        // ...and so on...
        default:
            throw new AppropriateException();
    }
}
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • So, can't this be achieved using cglib? – Akshat May 10 '14 at 13:15
  • 1
    @Akshat: Looks like it can, I found raphw's answer very interesting. Again, though, I would worry about performance problems with reflection when you have actual, measurable performance problems with reflection, not before. – T.J. Crowder May 10 '14 at 13:27
  • This was my first idea but having worked on systems with gargantuan case statements in the past and spending the time to replace them with a loop that iterates a CSV I'm interested in avoiding it. It is another option. I'd imagine the overhead of hitting the file system would be more intensive than just using reflection but I'm not sure. I guess it depends on the size of the class files being called. Going to give reflection a try and see how it does. Good advice. I can't think of another way offhand. – Jesse Ivy Apr 25 '17 at 22:41
2

Cglib comes with a class called FastMethod. This class's purpose is to invoke a given method by using a non-reflective interface. For this purpose, the FastMethod implements the interface and generates byte code for invoking the specified method, thus avoiding the supposedly expensive reflective invocation.

However, here are two reasons why you should most likely not use this class. Cglib was written quite a while ago. In these days, reflection was still more expensive then it is today. However, modern JVMs know a concept called inflation. By default, the JVM will generate byte code for invoking a method after its 15th reflective call. This is exactly what cglib offers you to do explicitly.

Furthermore, it is not the reflective call that is most expensive but it's lookup. You still need to name the method to represent as a FastMethod. Thus, you cannot avoid these costs even when using cglib.

I recommend you therefore to rely on reflection until you really identify this as a performance bottle neck. At least, use a tool like JMH to justify such an implementation. Also, consider that classes consume perm gen / meta space what can cause trouble for your users.

Community
  • 1
  • 1
Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
1

Since Java 1.7, there is a new way of invoking a method from its name via a MethodHandle, and it's much faster than reflection.

I did some benchmarking on a crappy laptop:

  1. Reflection can do about 4 million invocations per second
  2. MethodHandle can do about 140 million invocations per second

Either would be acceptable performance, but at just 7 nanoseconds average invocation time, using a MethodHandle is pretty darn good.

If you stored a reference to the MethodHandle in a HashMap against the name, you could reuse the for subsequent invocations.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • Did you use a harness for this benchmark? The difference should not be there. – Rafael Winterhalter May 12 '14 at 05:56
  • @raphw I wrote my own benchmark. I reused references to each type, so I only tested the speed of *invocation*. The speed difference is because MethodHandle avoids re-doing the virtual lookup step. A significant speed difference is expected. – Bohemian May 12 '14 at 07:43
  • The question is if this is what you normally want to achieve using reflection. A user might for example hand an instance to some framework whos class was dynamically subclassed by another framework. If you invoke a method non-virtually in this context, this can be rather messy. – Rafael Winterhalter May 12 '14 at 08:06
0

It is a common misconception that Reflection is slow. It was in the times of Java 1.3 or so.

But in modern Java Reflection is really well optimized. It uses dynamic bytecode generation under the hood. Besides, JVM can inline such calls straight into the caller, so the invocation of the methods with Reflection is nearly as fast as direct calls.

Other comments here suggest using cglib's FastMethod. In fact, it is not significantly faster than Reflection. Here is a benchmark written with well-known JMH framework:

@State(Scope.Benchmark)
public class MethodInvoke {
    Method method;
    FastMethod fastMethod;

    @Setup
    public void init() throws NoSuchMethodException {
        method = getClass().getMethod("foo", int.class, long.class);
        method.setAccessible(true);
        fastMethod = FastClass.create(getClass()).getMethod("foo", new Class[] { int.class, long.class });
    }

    @GenerateMicroBenchmark
    public long fastMethod() throws Exception {
        return (Long) fastMethod.invoke(this, new Object[] {2, 3L});
    }

    @GenerateMicroBenchmark
    public long reflection() throws Exception {
        return (Long) method.invoke(this, 2, 3L);
    }

    public long foo(int a, long b) {
        return a + b;
    }
}

Results on Java 7u51 (64-bit):

Benchmark                     Mode   Samples         Mean   Mean error    Units
b.MethodInvoke.fastMethod    thrpt         5    79248,583     3978,941   ops/ms
b.MethodInvoke.reflection    thrpt         5    76975,414     2844,730   ops/ms

You see, FastMethod.invoke is only 3% faster than Method.invoke, but in contrast to Reflection, FastMethod does not perform proper argument verification etc.

apangin
  • 92,924
  • 10
  • 193
  • 247
-1

Typically, introspective invocation on a method takes two stages: First you need to locate the target method to invoke and then you invoke the method on an target instance, providing it with parameters.

In this process, the most expensive operation is to locate the target method in a class (e.g. Method method = TargetClass.getMethod(Class[] signature ...) Once you get hold to the Method object, invoking the method on an object, like: method.invoke(targetObj, param,...) is a lightweight operation just marginally more expensive than direct method invocation.

To demonstrate this, I just did a quick & dirty comparison of the three methods with the following results (you need to compare them relative to each other):

  • Reflect method every time + invocation: 167ms
  • First cache methods from class + invocation: 36 ms
  • Direct invocation: 17ms

Note that introspection has a fix performance cost, so the more computation that the method is doing, the closer these numbers will be.

I have used this method-caching approach in previous projects where performance was highly important. In practice you observe that the actual method execution time makes the cost of introspection neglegible. (cfr . Amdahal's law )

Test code (bear the quick & dirtiness)

import java.lang.reflect.Method;
import java.util.Random;

/**
 * Created by maasg on 5/10/14.
 */
public class Instrospection {

    public static void main(String [] params) throws Exception {
        Random ran = new Random();
        String[] methods = new String[] {"method1", "method2", "method3"};
        Target target = new Target();

        // Warmup
        for (int i=0; i<1000; i++) {
            String methodName = methods[ran.nextInt(3)];
            String param = new Integer(ran.nextInt()).toString();
            Method method = Target.class.getMethod(methodName, String.class);
        }

        StringBuilder builder = new StringBuilder();
        long t0 = System.currentTimeMillis();
        for (int i=0; i<100000; i++) {
            String methodName = methods[ran.nextInt(3)];
            String param = new Integer(ran.nextInt()).toString();
            Method method = Target.class.getMethod(methodName, String.class);
            Object result = method.invoke(target, "param");
            builder.append(result.toString());
        }
        System.out.println("Elapsed 1: "+(System.currentTimeMillis()-t0));

        Method[] invokeMethods = new Method[] {
            Target.class.getMethod(methods[0], String.class),
            Target.class.getMethod(methods[1], String.class),
            Target.class.getMethod(methods[2], String.class),
        };

        builder = new StringBuilder();
        long t1 = System.currentTimeMillis();
        for (int i=0; i<100000; i++) {
            String param = new Integer(ran.nextInt()).toString();
            Method method = invokeMethods[ran.nextInt(3)];
            Object result = method.invoke(target, "param");
            builder.append(result.toString());
        }
        System.out.println("Elapsed 2: "+(System.currentTimeMillis()-t1));

        builder = new StringBuilder();
        long t2 = System.currentTimeMillis();
        for (int i=0; i<100000; i++) {
            Object result = null;
            String param = new Integer(ran.nextInt()).toString();
            switch (ran.nextInt(3)) {
                case 0: result = target.method1(param);
                case 1: result = target.method2(param);
                case 2: result = target.method3(param);
            }

            builder.append(result.toString());
        }
        System.out.println("Elapsed 3: "+(System.currentTimeMillis()-t2));

    }

}
maasg
  • 37,100
  • 11
  • 88
  • 115
  • You've been trapped by a common mistake of writing a wrong benchmark. Move your 3rd test before the first one, and the direct invocation will become "magically" slower than the cached reflective methods. The problems of writing a fair benchmark were discussed here thousand times, I'm not going to retell, instead I'll just leave a link to a [good benchmarking framework](http://openjdk.java.net/projects/code-tools/jmh/). – apangin May 10 '14 at 21:52
  • @apangin I wanted to sustain my explanation on method lookup vs call with some concrete numbers and, as you said, falled in the micro-benchmark trap. It's hard to make those 'from scratch'. I should know better :-) – maasg May 11 '14 at 13:23