mardi 19 septembre 2023

`MethodHandle` slower than Reflection when accessing Object

I would like to call a method via reflection in the most performant way possible.

The method returns an Object.

I've implemented this using both reflection and MethodHandles, I was expecting MethodHandle to be faster - but that's not what I'm seeing (~20-40% slower).

Take the following JMH benchmark:

import org.openjdk.jmh.annotations.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 200, time = 10, timeUnit = TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class AccessorBenchmark {
    private static final Object[] EMPTY_ARGS = new Object[0];

    private POJO source;
    private Method method;
    private MethodHandle methodHandle;
    private MethodHandle methodHandleModifiedReturnType;

    @Setup
    public void setup() throws ReflectiveOperationException {
        source = new POJO();

        method = source.getClass().getDeclaredMethod("getNumber");
        
        methodHandle = MethodHandles.lookup().unreflect(method);
        methodHandleModifiedReturnType = methodHandle.asType(methodHandle.type().changeReturnType(Number.class));
    }

    @Benchmark
    public Number reflection() throws Throwable {
        return (Number) method.invoke(source, EMPTY_ARGS);
    }

    @Benchmark
    public Number methodHandle() throws Throwable {
        return (Number) methodHandle.invoke(source);
    }

    @Benchmark
    public Number methodHandleInvokeExact() throws Throwable {
        return  (Number) methodHandleModifiedReturnType.invokeExact(source);
    }

    public class POJO {
        private final AtomicInteger counter = new AtomicInteger();

        public AtomicInteger getNumber() {
            return counter;
        }
    }
}

The following result is returned with Java 17:

Benchmark                                     Mode   Cnt   Score    Error   Units
AccessorBenchmark.methodHandle                avgt  1000   2.856 ±  0.004   ns/op
AccessorBenchmark.methodHandleInvokeExact     avgt  1000   2.359 ±  0.003   ns/op
AccessorBenchmark.reflection                  avgt  1000   2.017 ±  0.002   ns/op

Any ideas?





Aucun commentaire:

Enregistrer un commentaire