All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.bytebuddy.benchmark.ClassByExtensionBenchmark Maven / Gradle / Ivy

There is a newer version: 1.15.10
Show newest version
/*
 * Copyright 2014 - Present Rafael Winterhalter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.bytebuddy.benchmark;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.benchmark.specimen.ExampleClass;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.implementation.bind.annotation.*;
import net.bytebuddy.pool.TypePool;
import net.bytebuddy.utility.nullability.MaybeNull;
import net.sf.cglib.proxy.*;
import org.openjdk.jmh.annotations.*;

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.none;

/**
 * 

* This benchmark dynamically creates a subclass of {@link ExampleClass} which overrides all methods to invoke the * direct super class's implementation. The benchmark furthermore creates an instance of this class since some * code generation frameworks rely on this property. Because this benchmark requires the creation of a subclass, * the JDK proxy is not included in this benchmark. *

*

* Note that this class defines all values that are accessed by benchmark methods as instance fields. This way, the JIT * compiler's capability of constant folding is limited in order to produce more comparable test results. *

*/ @State(Scope.Thread) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) public class ClassByExtensionBenchmark { /** * The base class to be subclassed in all benchmarks. */ public static final Class BASE_CLASS = ExampleClass.class; /** * The base class to be subclassed in all benchmarks. */ private Class baseClass = BASE_CLASS; /** * The zero-length of the class loader's URL. */ private int urlLength = 0; /** * Creates a new class loader. By using a fresh class loader for each creation, we avoid name space issues. * A class loader's creation is part of the benchmark but since any test creates a class loader exactly once, * the benchmark remains valid. * * @return A new class loader. */ private ClassLoader newClassLoader() { return new URLClassLoader(new URL[urlLength]); } /** * An implementation to be used by {@link ClassByExtensionBenchmark#benchmarkByteBuddyWithProxyAndReusedDelegator()}. */ @MaybeNull private Implementation proxyInterceptor; /** * An implementation to be used by {@link ClassByExtensionBenchmark#benchmarkByteBuddyWithAccessorAndReusedDelegator()}. */ @MaybeNull private Implementation accessInterceptor; /** * An implementation to be used by {@link ClassByExtensionBenchmark#benchmarkByteBuddyWithPrefixAndReusedDelegator()}. */ @MaybeNull private Implementation.Composable prefixInterceptor; /** * A description of {@link ClassByExtensionBenchmark#baseClass}. */ @MaybeNull private TypeDescription baseClassDescription; /** * A description of {@link ByteBuddyProxyInterceptor}. */ @MaybeNull private TypeDescription proxyClassDescription; /** * A description of {@link ByteBuddyAccessInterceptor}. */ @MaybeNull private TypeDescription accessClassDescription; /** * A description of {@link ByteBuddyPrefixInterceptor}. */ @MaybeNull private TypeDescription prefixClassDescription; /** * A method delegation to {@link ByteBuddyProxyInterceptor}. */ @MaybeNull private Implementation proxyInterceptorDescription; /** * A method delegation to {@link ByteBuddyAccessInterceptor}. */ @MaybeNull private Implementation accessInterceptorDescription; /** * A method delegation to {@link ByteBuddyPrefixInterceptor}. */ @MaybeNull private Implementation.Composable prefixInterceptorDescription; /** * A setup method to create precomputed delegator. */ @Setup public void setup() { proxyInterceptor = MethodDelegation.to(ByteBuddyProxyInterceptor.class); accessInterceptor = MethodDelegation.to(ByteBuddyAccessInterceptor.class); prefixInterceptor = MethodDelegation.to(ByteBuddyPrefixInterceptor.class); baseClassDescription = TypePool.Default.ofSystemLoader().describe(baseClass.getName()).resolve(); proxyClassDescription = TypePool.Default.ofSystemLoader().describe(ByteBuddyProxyInterceptor.class.getName()).resolve(); accessClassDescription = TypePool.Default.ofSystemLoader().describe(ByteBuddyAccessInterceptor.class.getName()).resolve(); prefixClassDescription = TypePool.Default.ofSystemLoader().describe(ByteBuddyPrefixInterceptor.class.getName()).resolve(); proxyInterceptorDescription = MethodDelegation.to(proxyClassDescription); accessInterceptorDescription = MethodDelegation.to(accessClassDescription); prefixInterceptorDescription = MethodDelegation.to(prefixClassDescription); } /** * Creates a baseline for the benchmark. * * @return A simple object that is not transformed. */ @Benchmark public ExampleClass baseline() { return new ExampleClass(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark creates proxy classes for the invocation * of super methods which requires the creation of auxiliary classes. * * @return The created instance, in order to avoid JIT removal. * @throws java.lang.Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithProxy() throws Exception { return new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClass) .method(isDeclaredBy(baseClass)).intercept(MethodDelegation.to(ByteBuddyProxyInterceptor.class)) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark also uses the annotation-based approach * but creates delegation methods which do not require the creation of additional classes. This benchmark reuses a * precomputed delegator. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithProxyAndReusedDelegator() throws Exception { return new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClass) .method(isDeclaredBy(baseClass)).intercept(proxyInterceptor) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark creates proxy classes for the invocation * of super methods which requires the creation of auxiliary classes. This benchmark uses a type pool to compare against * usage of the reflection API. * * @return The created instance, in order to avoid JIT removal. * @throws java.lang.Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithProxyWithTypePool() throws Exception { return (ExampleClass) new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClassDescription) .method(isDeclaredBy(baseClassDescription)).intercept(MethodDelegation.to(proxyClassDescription)) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark also uses the annotation-based approach * but creates delegation methods which do not require the creation of additional classes. This benchmark reuses a * precomputed delegator. This benchmark uses a type pool to compare against usage of the reflection API. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithProxyAndReusedDelegatorWithTypePool() throws Exception { return (ExampleClass) new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClassDescription) .method(isDeclaredBy(baseClassDescription)).intercept(proxyInterceptorDescription) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark also uses the annotation-based approach * but creates delegation methods which do not require the creation of additional classes. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithAccessor() throws Exception { return new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClass) .method(isDeclaredBy(baseClass)).intercept(MethodDelegation.to(ByteBuddyAccessInterceptor.class)) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark also uses the annotation-based approach * but creates delegation methods which do not require the creation of additional classes. This benchmark reuses a * precomputed delegator. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithAccessorAndReusedDelegator() throws Exception { return new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClass) .method(isDeclaredBy(baseClass)).intercept(accessInterceptor) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark also uses the annotation-based approach * but creates delegation methods which do not require the creation of additional classes. This benchmark uses a type * pool to compare against usage of the reflection API. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithAccessorWithTypePool() throws Exception { return (ExampleClass) new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClassDescription) .method(isDeclaredBy(baseClassDescription)).intercept(MethodDelegation.to(accessClassDescription)) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark also uses the annotation-based approach * but creates delegation methods which do not require the creation of additional classes. This benchmark reuses a * precomputed delegator. This benchmark uses a type pool to compare against usage of the reflection API. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithAccessorAndReusedDelegatorWithTypePool() throws Exception { return (ExampleClass) new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClassDescription) .method(isDeclaredBy(baseClassDescription)).intercept(accessInterceptorDescription) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark uses delegation but completes with a * hard-coded super method call. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithPrefix() throws Exception { return new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClass) .method(isDeclaredBy(baseClass)).intercept(MethodDelegation.to(ByteBuddyPrefixInterceptor.class).andThen(SuperMethodCall.INSTANCE)) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark uses delegation but completes with a * hard-coded super method call. This benchmark reuses a precomputed delegator. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithPrefixAndReusedDelegator() throws Exception { return new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClass) .method(isDeclaredBy(baseClass)).intercept(prefixInterceptor.andThen(SuperMethodCall.INSTANCE)) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark uses delegation but completes with a * hard-coded super method call. This benchmark uses a type pool to compare against usage of the reflection API. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithPrefixWithTypePool() throws Exception { return (ExampleClass) new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClassDescription) .method(isDeclaredBy(baseClassDescription)).intercept(MethodDelegation.to(prefixClassDescription).andThen(SuperMethodCall.INSTANCE)) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark uses delegation but completes with a * hard-coded super method call. This benchmark reuses a precomputed delegator. This benchmark uses a type pool to * compare against usage of the reflection API. * * @return The created instance, in order to avoid JIT removal. * @throws Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddyWithPrefixAndReusedDelegatorWithTypePool() throws Exception { return (ExampleClass) new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClassDescription) .method(isDeclaredBy(baseClassDescription)).intercept(prefixInterceptorDescription.andThen(SuperMethodCall.INSTANCE)) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using Byte Buddy. This benchmark uses a specialized interception * strategy which is easier to inline by the compiler. * * @return The created instance, in order to avoid JIT removal. * @throws java.lang.Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkByteBuddySpecialized() throws Exception { return new ByteBuddy() .with(TypeValidation.DISABLED) .ignore(none()) .subclass(baseClass) .method(isDeclaredBy(baseClass)).intercept(SuperMethodCall.INSTANCE) .make() .load(newClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded() .getDeclaredConstructor() .newInstance(); } /** * Performs a benchmark of a class extension using cglib. * * @return The created instance, in order to avoid JIT removal. */ @Benchmark public ExampleClass benchmarkCglib() { Enhancer enhancer = new Enhancer(); enhancer.setUseCache(false); enhancer.setUseFactory(false); enhancer.setInterceptDuringConstruction(true); enhancer.setClassLoader(newClassLoader()); enhancer.setSuperclass(baseClass); CallbackHelper callbackHelper = new CallbackHelper(baseClass, new Class[0]) { protected Object getCallback(Method method) { if (method.getDeclaringClass() == baseClass) { return new MethodInterceptor() { public Object intercept(Object object, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(object, arguments); } }; } else { return NoOp.INSTANCE; } } }; enhancer.setCallbackFilter(callbackHelper); enhancer.setCallbacks(callbackHelper.getCallbacks()); return (ExampleClass) enhancer.create(); } /** * Performs a benchmark of a class extension using javassist proxies. * * @return The created instance, in order to avoid JIT removal. * @throws java.lang.Exception If the invocation causes an exception. */ @Benchmark public ExampleClass benchmarkJavassist() throws Exception { ProxyFactory proxyFactory = new ProxyFactory() { protected ClassLoader getClassLoader() { return newClassLoader(); } }; proxyFactory.setUseCache(false); proxyFactory.setUseWriteReplace(false); proxyFactory.setSuperclass(baseClass); proxyFactory.setFilter(new MethodFilter() { public boolean isHandled(Method method) { return method.getDeclaringClass() == baseClass; } }); @SuppressWarnings("unchecked") Object instance = proxyFactory.createClass().getDeclaredConstructor().newInstance(); ((javassist.util.proxy.Proxy) instance).setHandler(new MethodHandler() { public Object invoke(Object self, Method thisMethod, Method proceed, Object[] argument) throws Throwable { return proceed.invoke(self, argument); } }); return (ExampleClass) instance; } /** * Instead of using the {@link net.bytebuddy.implementation.SuperMethodCall} implementation, we are using * a delegate in order to emulate the interception approach of other instrumentation libraries. Otherwise, * this benchmark would be biased in favor of Byte Buddy. */ public static class ByteBuddyProxyInterceptor { /** * The interceptor's constructor is not supposed to be invoked. */ private ByteBuddyProxyInterceptor() { throw new UnsupportedOperationException(); } /** * Call the super method. * * @param zuper A proxy for invoking the super method. * @return The return value of the super method invocation. * @throws Exception As declared by {@link java.util.concurrent.Callable}'s contract. */ @RuntimeType public static Object intercept(@SuperCall Callable zuper) throws Exception { return zuper.call(); } } /** * Instead of using the {@link net.bytebuddy.implementation.SuperMethodCall} implementation, we are creating * delegate methods that allow the invocation of the original code. */ public static class ByteBuddyAccessInterceptor { /** * The interceptor's constructor is not supposed to be invoked. */ private ByteBuddyAccessInterceptor() { throw new UnsupportedOperationException(); } /** * Calls the super method. * * @param target The target instance. * @param arguments The arguments to the method. * @param method A method for invoking the original code. * @return The return value of the method. * @throws Exception If the super method call yields an exception. */ @RuntimeType public static Object intercept(@This Object target, @AllArguments Object[] arguments, @SuperMethod(privileged = false) Method method) throws Exception { return method.invoke(target, arguments); } } /** * An interceptor that is invoked prior to a super method call. */ public static class ByteBuddyPrefixInterceptor { /** * The interceptor's constructor is not supposed to be invoked. */ private ByteBuddyPrefixInterceptor() { throw new UnsupportedOperationException(); } /** * Invoked prior to a method call. */ public static void intercept() { /* do nothing */ } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy