Quantcast
Channel: Active questions tagged methodhandle - Stack Overflow
Viewing all articles
Browse latest Browse all 36

`MethodHandle` slower than Reflection when accessing primitive

$
0
0

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

The method returns a primitive long.

I've implemented this using both reflection and MethodHandles.

I was expecting MethodHandle to be faster because:

  • That's one of the benefits of MethodHandles
  • It avoid boxing/unboxing as you experience with reflection

But in all of my benchmarks, MethodHandles are slower (~2-5%-ish).

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.AtomicLong;@BenchmarkMode(Mode.AverageTime)@OutputTimeUnit(TimeUnit.NANOSECONDS)@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MILLISECONDS)@Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS)@State(Scope.Benchmark)public class AccessorBenchmark {    private POJO source;    private ReflectionAccessor reflectionAccessor;    private MethodHandleAccessor methodHandleAccessor;    @Setup    public void setup() throws ReflectiveOperationException {        source = new POJO();        final Method method = source.getClass().getDeclaredMethod("longMethod");        reflectionAccessor = new ReflectionAccessor(method);        methodHandleAccessor = new MethodHandleAccessor(method);    }    @Benchmark    public long reflectionAccessor() throws ReflectiveOperationException {        return reflectionAccessor.get(source);    }    @Benchmark    public long methodHandleAccessor() throws Throwable {        return methodHandleAccessor.get(source);    }    public class ReflectionAccessor {        private final Object[] EMPTY_ARGS = new Object[0];        private final Method method;        public ReflectionAccessor(final Method method) {            this.method = method;        }        public long get(final Object source) throws ReflectiveOperationException {            return ((Number) method.invoke(source, EMPTY_ARGS)).longValue();        }    }    public class MethodHandleAccessor {        private MethodHandle methodHandle;        public MethodHandleAccessor(final Method method) throws ReflectiveOperationException {            methodHandle = MethodHandles.lookup().unreflect(method);            methodHandle = methodHandle                    .asType(methodHandle.type().changeReturnType(long.class).changeParameterType(0, Object.class));        }        public long get(final Object source) throws Throwable {            return (long) methodHandle.invokeExact(source);        }    }    public class POJO {        // Chose a value outside of the autoboxing cache range        private final AtomicLong counter = new AtomicLong(Long.MAX_VALUE);        /** Some dummy method that returns different values in consistent amounts of time */        public long longMethod() {            return counter.addAndGet(-1);        }    }}

The following result is returned with Java 17:

Benchmark                                Mode  Cnt     Score    Error   UnitsAccessorBenchmark.methodHandleAccessor   avgt   25     4.204 ±  0.546   ns/opAccessorBenchmark.reflectionAccessor     avgt   25     4.123 ±  0.040   ns/op

Any ideas?


Viewing all articles
Browse latest Browse all 36

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>