17

Given a class Foo and a property bar, neither of which I know at compile time, I need to repeatedly call the getter Foo.getBar() many, many times.

Suppose I have:

Method barGetterMethod = ...; // Don't worry how I got this

And I need to do something like this:

for (Object foo : fooList) { // 1000000000 elements in fooList
    Object bar = barGetterMethod.invoke(foo);
    ...
}

The implementation above is still very slow compared to calling it without reflection. Is there a faster way?

What's the fastest way of calling a getter with reflection in Java?

M. Justin
  • 14,487
  • 7
  • 91
  • 130
Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
  • If there are so many elements, maybe it will be worth compiling (at the execution of your program) an `ad-hoc` class that wraps the object. Don't have used it at all, but I know it can be done. – SJuan76 Jan 03 '13 at 20:23
  • 1
    Reflection is generally going to be significantly slower than non-reflection. (This has changed _somewhat_ in more recent versions of Java.) – Louis Wasserman Jan 03 '13 at 20:31
  • It the range of classes and methods *really* unknown, or will it be within a known range? How many *different* combinations does your current properties file contain? – Bohemian Jan 03 '13 at 21:17
  • @Bohemian The range of classes and methods is relatively limited: I work on a framework (Drools Planner) and the user's classes are detected through annotations (much like Hibernate Annotations). Normally there are about 2-10 distinct methods I 'll call, but I 'll call some of those billions of times. – Geoffrey De Smet Jan 04 '13 at 09:46
  • I have an idea of how to get a fast impl, but it's a bit of work. If the accepted answer doesn't perform well enough, let me know and I'll work on the different approach. – Bohemian Jan 05 '13 at 02:46
  • @Bohemian The accepted answer is controversial indeed: not everyone is seeing the same benchmarks conclusions currently, and I even can't run it. I accepted it because it's clear that on the long term, MethodHandle's are the way to go. – Geoffrey De Smet Jan 06 '13 at 08:14
  • Agree MethodHandled sound great, but did you get acceptable benchmarks? – Bohemian Jan 06 '13 at 10:03
  • @Bohemian No. I didn't even get it to compile/run with the example code (which is due to a bug in openjdk 1.7.0_07 on linux I believe, so I 've reported it). – Geoffrey De Smet Jan 07 '13 at 10:00
  • I think the JVM will do its own [optimization](https://blogs.oracle.com/buck/entry/inflation_system_properties) after a number of rounds. – shmosel May 31 '16 at 15:44

3 Answers3

31

You might use a MethodHandle. Its Javadoc writes:

Using factory methods in the Lookup API, any class member represented by a Core Reflection API object can be converted to a behaviorally equivalent method handle. For example, a reflective Method can be converted to a method handle using Lookup.unreflect. The resulting method handles generally provide more direct and efficient access to the underlying class members.

While this will reduce the overhead, method handles still prevent certain optimizations (such a method inlining) the JVM could employ if the call were made with the usual (non-reflective) byte code instructions. Whether such optimizations would be beneficial depends on how you use the method (if that code path always invokes the same method, inlining can help, if it is a different method each time, probably not).

The following microbenchmark might give you a rough idea about the relative performance of reflection, method handles, and direct invocation:

package tools.bench;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.math.BigDecimal;

public abstract class Bench {

    final String name;

    public Bench(String name) {
        this.name = name;
    }

    abstract int run(int iterations) throws Throwable;

    private BigDecimal time() {
        try {
            int nextI = 1;
            int i;
            long duration;
            do {
                i = nextI;
                long start = System.nanoTime();
                run(i);
                duration = System.nanoTime() - start;
                nextI = (i << 1) | 1; 
            } while (duration < 100000000 && nextI > 0);
            return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }   

    @Override
    public String toString() {
        return name + "\t" + time() + " ns";
    }

    static class C {
        public Integer foo() {
            return 1;
        }
    }

    static final MethodHandle sfmh;

    static {
        try {
            Method m = C.class.getMethod("foo");
            sfmh = MethodHandles.lookup().unreflect(m);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        final C invocationTarget = new C();
        final Method m = C.class.getMethod("foo");
        final Method am = C.class.getMethod("foo");
        am.setAccessible(true);
        final MethodHandle mh = sfmh;

        Bench[] marks = {
            new Bench("reflective invocation (without setAccessible)") {
                @Override int run(int iterations) throws Throwable {
                    int x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x += (Integer) m.invoke(invocationTarget);
                    }
                    return x;
                }
            },
            new Bench("reflective invocation (with setAccessible)") {                   
                @Override int run(int iterations) throws Throwable {
                    int x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x += (Integer) am.invoke(invocationTarget);
                    }
                    return x;
                }
            },
            new Bench("methodhandle invocation") {
                @Override int run(int iterations) throws Throwable {
                    int x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x += (Integer) mh.invokeExact(invocationTarget);
                    }
                    return x;
                }
            },
            new Bench("static final methodhandle invocation") {
                @Override int run(int iterations) throws Throwable {
                    int x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x += (Integer) sfmh.invokeExact(invocationTarget);
                    }
                    return x;
                }
            },
            new Bench("direct invocation") {
                @Override int run(int iterations) throws Throwable {
                    int x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x += invocationTarget.foo();
                    }
                    return x;
                }
            },
        };
        for (Bench bm : marks) {
            System.out.println(bm);
        }
    }
}

on my somewhat dated notebook with

java version "1.7.0_02"
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
Java HotSpot(TM) Client VM (build 22.0-b10, mixed mode, sharing)

this prints:

reflective invocation (without setAccessible)   568.506 ns
reflective invocation (with setAccessible)  42.377 ns
methodhandle invocation 27.461 ns
static final methodhandle invocation    9.402 ns
direct invocation   9.363 ns

Update: As Irreputable points out, the server VM has somewhat different performance characteristics, so using a MethodHandle in a server VM will only help if you can put it in a static final field, in which case the VM can inline the call:

reflective invocation (without setAccessible)   9.736 ns
reflective invocation (with setAccessible)  7.113 ns
methodhandle invocation 26.319 ns
static final methodhandle invocation    0.045 ns
direct invocation   0.044 ns

I recommend that you measure your particular use case.

Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
meriton
  • 68,356
  • 14
  • 108
  • 175
  • 2
    My test shows that MethodHandle is even slower than Method. Unless it is `static final`, then VM can convert `mh.invokeExact(foo)` bytecode to `foo.getBar()` bytecode. – irreputable Jan 03 '13 at 21:20
  • what if you switch the order of reflection/methodHandle benches? – irreputable Jan 04 '13 at 00:10
  • in this use case, there should be no reason MethodHandle can be much faster than reflection, or reflection should be able to use the same technique to improve performance, which is what everybody wants. The `invokevirtual` thing seems to work its magic only for `static final MethodHandle` – irreputable Jan 04 '13 at 00:14
  • Changing the benchmark order to run the methodHandle first does not materially affect the results (still 28.6 ns for methodHandle and 44.1 for reflection with setaccessible). – meriton Jan 04 '13 at 00:20
  • As for why MethodHandle should be faster, I wondered that, too. Since a MethodHandle is only obtained for invocation, but a Method can be requested for many things, the MethodHandle might cache information only relevant to the invocation, or in a format more suitable for invocation. Additionally, a method handle invocation has a [polymorphic signature](http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandle.html#sigpoly), which can reduce the necessary amount of type conversions, and eliminate the need to create an array to hold the parameters. – meriton Jan 04 '13 at 00:43
  • right, invokeExact() retains more compile time info, which can be helpful. However, running your code on my PC, the numbers I got are `6.8, 4.5, 22.5, 0.0`, i.e. my reflection is much much faster. The main difference is probably because mine is on the "server" mode. – irreputable Jan 04 '13 at 01:04
  • the interesting thing is that MethodHandle does not get more optimization in the server mode; it's less optimized than Method. MethodHandle is probably just not intended for our use case. If I move it to `static final`, then it's optimized to direct invocation, costing 0ns. – irreputable Jan 04 '13 at 01:10
  • I can confirm that, thanks for pointing it out. I have included it in my answer. – meriton Jan 04 '13 at 02:02
  • Brilliant, but this code doesn't do any warmup it seems? Without a warmup (preferably 5 times the entire code like in the other answer), the results are meaningless. I 'll try that now and add an edit to the answer. – Geoffrey De Smet Jan 04 '13 at 09:10
  • I am getting `WrongMethodTypeException: ()Ljava/lang/Integer; cannot be called as ([Ljava/lang/Object;)Ljava/lang/Object;` on this code, anyone else getting that? OpenJDK 1.7 – Geoffrey De Smet Jan 04 '13 at 09:44
  • Replacing invokeExact with invokeWithArguments makes it run, but - even with warmups - I am also seeing 35ns for static final method handle, 4ns for reflective and 0 for direct. So MethodHandle is slower for me, even with the static final, but note that I am not using invokeExact (Linux, openjdk 1.7.0_07) – Geoffrey De Smet Jan 04 '13 at 11:29
  • The warmup occurs in the loop in `Bench.time()`. Only the best result for each benchmark is reported. The WrongTypeException indicates a type mismatch between method invocation and declaration. No, I am not getting that; execution completes normally on my notebook (did you try the current version? Have you changed anything?). – meriton Jan 04 '13 at 12:38
  • @meriton I didn't change anything. I just tried it again, copy the entire code again, same result (WrongMethodTypeException). What OS and java version (`java -version`) are you using? I am getting it with and without `-server`. – Geoffrey De Smet Jan 04 '13 at 13:28
  • 1
    Windows XP, java -version is in my answer. – meriton Jan 04 '13 at 14:53
11

Calling barReadMethod.setAccessible(true); turns off the security checks which can make it a bit faster. Even if it is accessible, it has to check otherwise.

If I run use a getter method with and without accessible true.

class Main {
    static class A {
        private final Integer i;

        A(Integer i) {
            this.i = i;
        }

        public Integer getI() {
            return i;
        }
    }

    public static void main(String... args) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        A[] as = new A[100000];
        for (int i = 0; i < as.length; i++)
            as[i] = new A(i);

        for (int i = 0; i < 5; i++) {
            long time1 = timeSetAccessible(as);
            long time2 = timeNotSetAccessible(as);
            System.out.printf("With setAccessible true %.1f ns, Without setAccessible %.1f ns%n",
                   (double) time1 / as.length, (double) time2 / as.length);
        }
    }

    static long dontOptimiseAvay = 0;

    private static long timeSetAccessible(A[] as) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Method getter = A.class.getDeclaredMethod("getI");
        getter.setAccessible(true);
        dontOptimiseAvay = 0;
        long start = System.nanoTime();
        for (A a : as) {
            dontOptimiseAvay += (Integer) getter.invoke(a);
        }
        return System.nanoTime() - start;
    }

    private static long timeNotSetAccessible(A[] as) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Method getter = A.class.getDeclaredMethod("getI");
//        getter.setAccessible(true);
        dontOptimiseAvay = 0;
        long start = System.nanoTime();
        for (A a : as) {
            dontOptimiseAvay += (Integer) getter.invoke(a);
        }
        return System.nanoTime() - start;
    }
}

prints

With setAccessible true 106.4 ns, Without setAccessible 126.9 ns
With setAccessible true 5.4 ns, Without setAccessible 29.4 ns
With setAccessible true 3.2 ns, Without setAccessible 9.9 ns
With setAccessible true 3.1 ns, Without setAccessible 9.0 ns
With setAccessible true 3.1 ns, Without setAccessible 8.9 ns

For a simple getter, using setAccessible(true) can be three times faster.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

If the static final MethodHandle option discussed above is not practical/possible, another option would be to dynamically generate a class using bytebuddy which has a single method taking foo, invoking the bar method on foo and returning the result.

This would provide performance on individual invocations of bar being essentially the same as direct invocation (the wrapped call would likely eventually be in-lined).

However, this would incur 1 time cost of generating the byte code for class and method. The cost for this is in the neighborhood of 200ns according to bytebuddy site.

Brett Okken
  • 6,210
  • 1
  • 19
  • 25
  • Hibernate uses a similar approach, with CGLib IIRC. – Geoffrey De Smet Jun 01 '16 at 06:35
  • While cglib and bytebuddy accomplish very similar goals, the means are pretty different. In my use, these differences lead to bytebuddy performing better. Considering that we are discussing the difference in nanoseconds on a per execution basis, bytebuddy is likely going to be significantly faster overall. – Brett Okken Jun 02 '16 at 13:12
  • Good to know. Are you talking about code generation performance (which is usually a bootstrap cost) or the actual speed of the generated code (which is a continuous runtime cost)? – Geoffrey De Smet Jun 03 '16 at 07:33