Created
March 14, 2017 15:47
-
-
Save anonymous/8777ffbfd3a39c1a3f61eb4d64711c8f to your computer and use it in GitHub Desktop.
Benchmark getting caller's class on Java 8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package benchmark.throwing; | |
import sun.reflect.Reflection; | |
import java.util.Random; | |
import java.util.concurrent.TimeUnit; | |
import java.util.function.Supplier; | |
import java.lang.invoke.MethodHandle; | |
import java.lang.invoke.MethodHandleProxies; | |
import java.lang.invoke.MethodHandles; | |
import java.lang.invoke.MethodHandles.Lookup; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import org.openjdk.jmh.annotations.Benchmark; | |
import org.openjdk.jmh.annotations.BenchmarkMode; | |
import org.openjdk.jmh.annotations.Fork; | |
import org.openjdk.jmh.annotations.Level; | |
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.Setup; | |
import org.openjdk.jmh.annotations.State; | |
import org.openjdk.jmh.annotations.Warmup; | |
import benchmarker.main.Bench; | |
@State(Scope.Thread) | |
@Fork(1) | |
@BenchmarkMode(Mode.AverageTime) | |
@OutputTimeUnit(TimeUnit.NANOSECONDS) | |
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) | |
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) | |
public class GetCallerClass { | |
interface StackTrace1 { | |
static Class<?> impl() { | |
Class<?> clazz = null; | |
for(StackTraceElement element : new Throwable().fillInStackTrace().getStackTrace()) | |
try { | |
if(clazz == null & (clazz = Class.forName(element.getClassName())) != StackTrace1.class) | |
clazz = null; | |
else if(clazz != StackTrace1.class) break; | |
} catch(ClassNotFoundException e) { | |
throw new AssertionError(e); | |
} | |
return clazz; | |
} | |
} | |
interface StackTrace2 { | |
static Class<?> impl() { | |
Class<?> clazz = null; | |
for(StackTraceElement element : new Throwable().getStackTrace()) | |
try { | |
if(clazz == null & (clazz = Class.forName(element.getClassName())) != StackTrace2.class) | |
clazz = null; | |
else if(clazz != StackTrace2.class) break; | |
} catch(ClassNotFoundException e) { | |
throw new AssertionError(e); | |
} | |
return clazz; | |
} | |
} | |
interface StackTrace3 { | |
public final static class MyThrowable extends Throwable { | |
public static final MyThrowable INSTANCE = new MyThrowable(); | |
MyThrowable() { | |
super("", null, false, true); | |
} | |
public final synchronized @Override Throwable fillInStackTrace() { | |
return getMessage() == "" ? super.fillInStackTrace() : this; | |
} | |
} | |
static Class<?> impl() { | |
StackTraceElement[] trace; | |
synchronized(MyThrowable.INSTANCE) { | |
trace = MyThrowable.INSTANCE.fillInStackTrace().getStackTrace(); | |
} | |
Class<?> clazz = null; | |
for(StackTraceElement element : trace) | |
try { | |
if(clazz == null & (clazz = Class.forName(element.getClassName())) != StackTrace3.class) | |
clazz = null; | |
else if(clazz != StackTrace3.class) break; | |
} catch(ClassNotFoundException e) { | |
throw new AssertionError(e); | |
} | |
return clazz; | |
} | |
} | |
interface StackTrace4 { | |
static Throwable IT = new Throwable(); | |
static Class<?> impl() { | |
StackTraceElement[] trace; | |
synchronized(IT) { | |
trace = IT.fillInStackTrace().getStackTrace(); | |
} | |
Class<?> clazz = null; | |
for(StackTraceElement element : trace) | |
try { | |
if(clazz == null & (clazz = Class.forName(element.getClassName())) != StackTrace4.class) | |
clazz = null; | |
else if(clazz != StackTrace4.class) break; | |
} catch(ClassNotFoundException e) { | |
throw new AssertionError(e); | |
} | |
return clazz; | |
} | |
} | |
interface Security { | |
static Class<?> impl() { | |
return MySecurityManager.INSTANCE.pleaseGetClassContext()[2]; | |
} | |
public static class MySecurityManager extends SecurityManager { | |
public static final MySecurityManager INSTANCE = new MySecurityManager(); | |
public Class<?>[] pleaseGetClassContext() { | |
return getClassContext(); | |
} | |
} | |
} | |
interface MethodInvoke { | |
static final Method getStackTraceElement = method(); | |
static final Throwable obj = new Throwable(); | |
static Method method() { | |
try { | |
Method method = Throwable.class.getDeclaredMethod("getStackTraceElement", Integer.TYPE); | |
method.setAccessible(true); | |
return method; | |
} catch(RuntimeException | NoSuchMethodException e) { | |
throw new AssertionError(e); | |
} | |
} | |
static Class<?> impl() { | |
try { | |
StackTraceElement element = (StackTraceElement) getStackTraceElement.invoke(obj, 2); | |
String className = element.getClassName(); | |
Class<?> forName = Class.forName(className); | |
return forName; | |
} catch(RuntimeException | ClassNotFoundException | IllegalAccessException | InvocationTargetException e) { | |
throw new AssertionError(e); | |
} | |
} | |
} | |
public interface HandleInvoke { | |
public StackTraceElement apply(Throwable t, int index); | |
static final HandleInvoke getStackTraceElement = method(); | |
static final Throwable obj = new Throwable(); | |
static HandleInvoke method() { | |
try { | |
Method method = Throwable.class.getDeclaredMethod("getStackTraceElement", Integer.TYPE); | |
method.setAccessible(true); | |
Class<HandleInvoke> intfc = HandleInvoke.class; | |
Lookup lookup = MethodHandles.lookup(); | |
MethodHandle bindTo = lookup.unreflect(method); | |
HandleInvoke as = MethodHandleProxies.asInterfaceInstance(intfc, bindTo); | |
return as; | |
} catch(RuntimeException | IllegalAccessException | NoSuchMethodException e) { | |
throw new AssertionError(e); | |
} | |
} | |
static Class<?> impl() { | |
try { | |
StackTraceElement apply = getStackTraceElement.apply(obj,2); | |
String className = apply.getClassName(); | |
return Class.forName(className); | |
} catch(ClassNotFoundException e) { | |
throw new AssertionError(e); | |
} | |
} | |
} | |
public interface ReflectionGet { | |
static @SuppressWarnings("deprecation") Class<?> impl() { | |
return Reflection.getCallerClass(2); | |
} | |
} | |
private int order; | |
private Random random; | |
public @Setup(Level.Iteration) void setup() {random = new Random(4321);} | |
private static final Supplier<Class<?>> exception1 = new Supplier<Class<?>>() {public @Override Class<?> get() {return StackTrace1 .impl();}}; | |
private static final Supplier<Class<?>> exception2 = new Supplier<Class<?>>() {public @Override Class<?> get() {return StackTrace2 .impl();}}; | |
private static final Supplier<Class<?>> exception3 = new Supplier<Class<?>>() {public @Override Class<?> get() {return StackTrace3 .impl();}}; | |
private static final Supplier<Class<?>> exception4 = new Supplier<Class<?>>() {public @Override Class<?> get() {return StackTrace4 .impl();}}; | |
private static final Supplier<Class<?>> baseline = new Supplier<Class<?>>() {public @Override Class<?> get() {return getClass ();}}; | |
private static final Supplier<Class<?>> security = new Supplier<Class<?>>() {public @Override Class<?> get() {return Security .impl();}}; | |
private static final Supplier<Class<?>> invokeMethod = new Supplier<Class<?>>() {public @Override Class<?> get() {return MethodInvoke .impl();}}; | |
private static final Supplier<Class<?>> invokeHandle = new Supplier<Class<?>>() {public @Override Class<?> get() {return HandleInvoke .impl();}}; | |
private static final Supplier<Class<?>> reflection = new Supplier<Class<?>>() {public @Override Class<?> get() {return ReflectionGet.impl();}}; | |
public @Benchmark Class<?> baseline () {return stacksGo(baseline );} | |
public @Benchmark Class<?> custom_SecurityManager_getClassContext () {return stacksGo(security );} | |
public @Benchmark Class<?> exception_Throwable_getStackTrace_v1 () {return stacksGo(exception1 );} | |
public @Benchmark Class<?> exception_Throwable_getStackTrace_v2 () {return stacksGo(exception2 );} | |
public @Benchmark Class<?> exception_Throwable_getStackTrace_v3 () {return stacksGo(exception3 );} | |
public @Benchmark Class<?> exception_Throwable_getStackTrace_v4 () {return stacksGo(exception4 );} | |
public @Benchmark Class<?> invokeMethod_Throwable_getStackTraceElement() {return stacksGo(invokeMethod);} | |
public @Benchmark Class<?> invokeHandle_Throwable_getStackTraceElement() {return stacksGo(invokeHandle);} | |
public @Benchmark Class<?> static_Reflection_getCallerClass () {return stacksGo(reflection );} | |
private Class<?> stacksGo(Supplier<Class<?>> s) {order=random.nextInt()|1; return stackBeg(0, s);} | |
private Class<?> stackBeg(int num, Supplier<Class<?>> s) {return order << num/2 << (num-num/2) >> 31 == 0 ? stack_03(num+1, s): stack_47(num+1, s);} | |
private Class<?> stack_03(int num, Supplier<Class<?>> s) {return order << num/2 << (num-num/2) >> 31 == 0 ? stack_01(num+1, s): stack_23(num+1, s);} | |
private Class<?> stack_47(int num, Supplier<Class<?>> s) {return order << num/2 << (num-num/2) >> 31 == 0 ? stack_45(num+1, s): stack_67(num+1, s);} | |
private Class<?> stack_01(int num, Supplier<Class<?>> s) {return order << num/2 << (num-num/2) >> 31 == 0 ? stackAt0(num+1, s): stackAt1(num+1, s);} | |
private Class<?> stack_23(int num, Supplier<Class<?>> s) {return order << num/2 << (num-num/2) >> 31 == 0 ? stackAt2(num+1, s): stackAt3(num+1, s);} | |
private Class<?> stack_45(int num, Supplier<Class<?>> s) {return order << num/2 << (num-num/2) >> 31 == 0 ? stackAt4(num+1, s): stackAt5(num+1, s);} | |
private Class<?> stack_67(int num, Supplier<Class<?>> s) {return order << num/2 << (num-num/2) >> 31 == 0 ? stackAt6(num+1, s): stackAt7(num+1, s);} | |
private Class<?> stackAt0(int num, Supplier<Class<?>> s) {return stackEnd(num, s);} | |
private Class<?> stackAt1(int num, Supplier<Class<?>> s) {return stackEnd(num, s);} | |
private Class<?> stackAt2(int num, Supplier<Class<?>> s) {return stackEnd(num, s);} | |
private Class<?> stackAt3(int num, Supplier<Class<?>> s) {return stackEnd(num, s);} | |
private Class<?> stackAt4(int num, Supplier<Class<?>> s) {return stackEnd(num, s);} | |
private Class<?> stackAt5(int num, Supplier<Class<?>> s) {return stackEnd(num, s);} | |
private Class<?> stackAt6(int num, Supplier<Class<?>> s) {return stackEnd(num, s);} | |
private Class<?> stackAt7(int num, Supplier<Class<?>> s) {return stackEnd(num, s);} | |
private Class<?> stackEnd(int num, Supplier<Class<?>> s) {return order << num/2 << (num-num/2) == 0 ? s.get(): stackBeg(num+1, s);} | |
// Benchmark Mode Cnt Score Error Units | |
// GetCallerClass.baseline avgt 5 181.628 ± 1.945 ns/op | |
// GetCallerClass.custom_SecurityManager_getClassContext avgt 5 3338.956 ± 29.751 ns/op | |
// GetCallerClass.exception_Throwable_getStackTrace_v1 avgt 5 52768.248 ± 928.263 ns/op | |
// GetCallerClass.exception_Throwable_getStackTrace_v2 avgt 5 49930.420 ± 774.868 ns/op | |
// GetCallerClass.exception_Throwable_getStackTrace_v3 avgt 5 49911.325 ± 566.927 ns/op | |
// GetCallerClass.exception_Throwable_getStackTrace_v4 avgt 5 49530.699 ± 773.626 ns/op | |
// GetCallerClass.invokeHandle_Throwable_getStackTraceElement avgt 5 1738.658 ± 21.474 ns/op | |
// GetCallerClass.invokeMethod_Throwable_getStackTraceElement avgt 5 1633.620 ± 33.011 ns/op | |
// GetCallerClass.static_Reflection_getCallerClass avgt 5 313.214 ± 5.901 ns/op | |
public static void main(String[] args) { | |
GetCallerClass o = new GetCallerClass(); | |
o.setup(); | |
System.out.println(o.exception_Throwable_getStackTrace_v1()); | |
System.out.println(o.exception_Throwable_getStackTrace_v2()); | |
System.out.println(o.exception_Throwable_getStackTrace_v3()); | |
System.out.println(o.exception_Throwable_getStackTrace_v4()); | |
System.out.println(o.baseline()); | |
System.out.println(o.custom_SecurityManager_getClassContext()); | |
System.out.println(o.invokeMethod_Throwable_getStackTraceElement()); | |
System.out.println(o.invokeHandle_Throwable_getStackTraceElement()); | |
System.out.println(o.static_Reflection_getCallerClass()); | |
Bench.me().run(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment