Geoffrey De Smet
2018-02-19 10:42:51 UTC
Hi guys,
I ran the following JMH benchmark on JDK 9 and JDK 8.
Source code and detailed results below.
Benchmark on JDK 9 Score
staticMethodHandle 2.770
lambdaMetafactory 3.052 // 10% slower
nonStaticMethodHandle 5.250 // 90% slower
Why is LambdaMetafactory 10% slower than a static MethodHandle
but 80% faster than a non-static MethodHandle?
Source code (copy paste ready)
====================
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
//Benchmark on JDK 9 Mode Cnt Score Error Units
//staticMethodHandle avgt 30 2.770 ± 0.023 ns/op // Baseline
//lambdaMetafactory avgt 30 3.052 ± 0.004 ns/op // 10% slower
//nonStaticMethodHandle avgt 30 5.250 ± 0.137 ns/op // 90% slower
//Benchmark on JDK 8 Mode Cnt Score Error Units
//staticMethodHandle avgt 30 2.772 ± 0.022 ns/op // Baseline
//lambdaMetafactory avgt 30 3.060 ± 0.007 ns/op // 10% slower
//nonStaticMethodHandle avgt 30 5.037 ± 0.022 ns/op // 81% slower
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class LamdaMetafactoryWeirdPerformance {
//
************************************************************************
// Set up of the 3 approaches.
//
************************************************************************
// Unusable for Java framework developers. Only usable by JVM
language developers. Baseline.
private static final MethodHandle staticMethodHandle;
// Usuable for Java framework developers. 30% slower
private final Function lambdaMetafactoryFunction;
// Usuable for Java framework developers. 100% slower
private final MethodHandle nonStaticMethodHandle;
static {
// Static MethodHandle setup
try {
staticMethodHandle = MethodHandles.lookup()
.findVirtual(Dog.class, "getName",
MethodType.methodType(String.class))
.asType(MethodType.methodType(Object.class,
Object.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
public LamdaMetafactoryWeirdPerformance() {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
// LambdaMetafactory setup
CallSite site = LambdaMetafactory.metafactory(lookup,
"apply",
MethodType.methodType(Function.class),
MethodType.methodType(Object.class, Object.class),
lookup.findVirtual(Dog.class, "getName",
MethodType.methodType(String.class)),
MethodType.methodType(String.class, Dog.class));
lambdaMetafactoryFunction = (Function)
site.getTarget().invokeExact();
// Non-static MethodHandle setup
nonStaticMethodHandle = lookup
.findVirtual(Dog.class, "getName",
MethodType.methodType(String.class))
.asType(MethodType.methodType(Object.class,
Object.class));
} catch (Throwable e) {
throw new IllegalStateException(e);
}
}
//
************************************************************************
// Benchmark
//
************************************************************************
private Object dogObject = new Dog("Fido");
@Benchmark
public Object _1_staticMethodHandle() throws Throwable {
return staticMethodHandle.invokeExact(dogObject);
}
@Benchmark
public Object _2_lambdaMetafactory() {
return lambdaMetafactoryFunction.apply(dogObject);
}
@Benchmark
public Object _3_nonStaticMethodHandle() throws Throwable {
return nonStaticMethodHandle.invokeExact(dogObject);
}
private static class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
With kind regards,
Geoffrey De Smet
I ran the following JMH benchmark on JDK 9 and JDK 8.
Source code and detailed results below.
Benchmark on JDK 9 Score
staticMethodHandle 2.770
lambdaMetafactory 3.052 // 10% slower
nonStaticMethodHandle 5.250 // 90% slower
Why is LambdaMetafactory 10% slower than a static MethodHandle
but 80% faster than a non-static MethodHandle?
Source code (copy paste ready)
====================
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
//Benchmark on JDK 9 Mode Cnt Score Error Units
//staticMethodHandle avgt 30 2.770 ± 0.023 ns/op // Baseline
//lambdaMetafactory avgt 30 3.052 ± 0.004 ns/op // 10% slower
//nonStaticMethodHandle avgt 30 5.250 ± 0.137 ns/op // 90% slower
//Benchmark on JDK 8 Mode Cnt Score Error Units
//staticMethodHandle avgt 30 2.772 ± 0.022 ns/op // Baseline
//lambdaMetafactory avgt 30 3.060 ± 0.007 ns/op // 10% slower
//nonStaticMethodHandle avgt 30 5.037 ± 0.022 ns/op // 81% slower
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class LamdaMetafactoryWeirdPerformance {
//
************************************************************************
// Set up of the 3 approaches.
//
************************************************************************
// Unusable for Java framework developers. Only usable by JVM
language developers. Baseline.
private static final MethodHandle staticMethodHandle;
// Usuable for Java framework developers. 30% slower
private final Function lambdaMetafactoryFunction;
// Usuable for Java framework developers. 100% slower
private final MethodHandle nonStaticMethodHandle;
static {
// Static MethodHandle setup
try {
staticMethodHandle = MethodHandles.lookup()
.findVirtual(Dog.class, "getName",
MethodType.methodType(String.class))
.asType(MethodType.methodType(Object.class,
Object.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
public LamdaMetafactoryWeirdPerformance() {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
// LambdaMetafactory setup
CallSite site = LambdaMetafactory.metafactory(lookup,
"apply",
MethodType.methodType(Function.class),
MethodType.methodType(Object.class, Object.class),
lookup.findVirtual(Dog.class, "getName",
MethodType.methodType(String.class)),
MethodType.methodType(String.class, Dog.class));
lambdaMetafactoryFunction = (Function)
site.getTarget().invokeExact();
// Non-static MethodHandle setup
nonStaticMethodHandle = lookup
.findVirtual(Dog.class, "getName",
MethodType.methodType(String.class))
.asType(MethodType.methodType(Object.class,
Object.class));
} catch (Throwable e) {
throw new IllegalStateException(e);
}
}
//
************************************************************************
// Benchmark
//
************************************************************************
private Object dogObject = new Dog("Fido");
@Benchmark
public Object _1_staticMethodHandle() throws Throwable {
return staticMethodHandle.invokeExact(dogObject);
}
@Benchmark
public Object _2_lambdaMetafactory() {
return lambdaMetafactoryFunction.apply(dogObject);
}
@Benchmark
public Object _3_nonStaticMethodHandle() throws Throwable {
return nonStaticMethodHandle.invokeExact(dogObject);
}
private static class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
With kind regards,
Geoffrey De Smet