欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

使用JMH做Benchmark基准测试

程序员文章站 2022-04-09 16:07:54
前言 JDK,CGLIB,JAVASSIST是常用的动态代理方式。 JDK动态代理仅能对具有接口的类进行代理。 CGLIB动态代理方式的目标类可以没有接口。 Javassist是一个开源的分析、编辑和创建Java字节码的类库,JAVASSIST可以动态修改类,比如添加方法和属性。JAVASSIST的 ......

 

前言

jdk,cglib,javassist是常用的动态代理方式。

jdk动态代理仅能对具有接口的类进行代理。

cglib动态代理方式的目标类可以没有接口。

javassist是一个开源的分析、编辑和创建java字节码的类库,javassist可以动态修改类,比如添加方法和属性。javassist的目标类也没有接口限制。

动态代理常用在rpc接口调用中,因此选择一个好的动态代理方式,会对系统性能有一定的提升。

 

对于代码的性能测试,常规的方法如下,如此是无法获取到准确的性能数据的

long start = system.currenttimemillis();
xxx.xx();
long end = system.currenttimemillis();
system.out.println("运行时间:"+(end-start));

 

jmh用来做基准测试,由于jit编译器会根据代码运行情况进行优化,代码在第一次执行的时候,速度相对较慢,随着运行的次数增加,jit编译器会对代码进行优化,以达到最佳的性能状态。

jmh可以对代码进行预热,让代码达到最佳的性能状态,再进行性能测试。

更详细的说明参考: 使用jmh做benchmark基准测试

 

本博客主要讲解使用jmh对这三种动态代理的对象创建过程和方法调用进行测试。jdk版本是8.0.

代理实现

jdk方式

public class jdkinvocationhandler  implements invocationhandler {

    private object target = null;
    public jdkinvocationhandler(object object){
        this.target = object;
    }


    public object invoke(object proxy, method method, object[] args) throws throwable {

        object result = method.invoke(this.target,args);
        return result;
    }

    public  object getproxy(){
        object object =    proxy.newproxyinstance(
                this.target.getclass().getclassloader(),
                this.target.getclass().getinterfaces(),
                this);
        return  object;
    }

}

 

cglib方式

引入pom

     <dependency>
            <groupid>cglib</groupid>
            <artifactid>cglib</artifactid>
            <version>3.2.5</version>
        </dependency>

 

代码

public class cglibproxy  implements methodinterceptor {

    private enhancer enhancer = new enhancer();

    public  object getproxy(class clazz){
        enhancer.setsuperclass(clazz);
        enhancer.setcallback(this);
        return enhancer.create();

    }
    public object intercept(object o, method method, object[] objects, methodproxy methodproxy) throws throwable {

        object result = methodproxy.invokesuper(o,objects);

        return result;
    }
}

 

javassist方式

pom

    <dependency>
            <groupid>org.openjdk.jmh</groupid>
            <artifactid>jmh-core</artifactid>
            <version>1.20</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupid>org.openjdk.jmh</groupid>
            <artifactid>jmh-generator-annprocess</artifactid>
            <version>1.20</version>
        </dependency>

 

代码

public class javassistproxy {

    public <t> t getproxy(class<t> interfaceclass){
        proxyfactory proxyfactory  = new proxyfactory();

        if(interfaceclass.isinterface()){
            class[] clz  = new class[1];
            clz[0] = interfaceclass;
            proxyfactory.setinterfaces(clz);
        }
        else {
            proxyfactory.setsuperclass(interfaceclass);
        }
        proxyfactory.sethandler(new methodhandler() {
            public object invoke(object proxy, method method, method method1, object[] args) throws throwable {
                object result = method1.invoke(proxy,args);
                return  result;
            }
        });
        try{
            t bean =  (t)proxyfactory.createclass().newinstance();
            return  bean;
        }
        catch(exception ex){
            log.error("javassit 创建代理失败:{}",ex.getmessage());
            return null;
        }
    }

}

 

性能测试

创建性能测试

@benchmarkmode(mode.averagetime)
@outputtimeunit(timeunit.nanoseconds)
public class proxycreatetest {

    public static void main(string args[]) throws exception{

        options ops = new optionsbuilder().include(proxycreatetest.class.getsimplename())
                .forks(1).build();
        new runner(ops).run();
    }

    @benchmark
    public void cglibproxycreate(){
        proxyservice proxyservice =  (proxyservice)new cglibproxy().getproxy(proxyserviceimpl.class);
    }


   @benchmark
    public void jdkproxycreate(){
       proxyservice proxyservice = (proxyservice) new jdkinvocationhandler(new proxyserviceimpl()).getproxy();
    }

    @benchmark
    public void javassistproxycreate(){
        proxyservice proxyservice = (proxyservice)  new javassistproxy().getproxy(proxyserviceimpl.class);
    }

}

 

 测试结果

  第一次测试
* benchmark mode cnt score error units * proxycreatetest.cglibproxycreate avgt 20 192.691 ± 5.962 ns/op * proxycreatetest.javassistproxycreate avgt 20 2741254.026 ± 334384.484 ns/op * proxycreatetest.jdkproxycreate avgt 20 130.982 ± 14.467 ns/op * * 第二次测试 * benchmark mode cnt score error units * proxycreatetest.cglibproxycreate avgt 20 212.150 ± 15.399 ns/op * proxycreatetest.javassistproxycreate avgt 20 2995729.108 ± 265629.897 ns/op * proxycreatetest.jdkproxycreate avgt 20 124.842 ± 8.404 ns/op *
第三次测试 * benchmark mode cnt score error units * proxycreatetest.cglibproxycreate avgt 20 206.603 ± 6.834 ns/op * proxycreatetest.javassistproxycreate avgt 20 2979335.282 ± 290935.626 ns/op * proxycreatetest.jdkproxycreate avgt 20 129.260 ± 9.020 ns/op

 

从测试结果来看,javassist的代理对象创建性能最差。最好的是jdk方式。

调用性能测试

//所有测试线程共享一个实例
@state(scope.benchmark)
//调用的平均时间,例如“每次调用平均耗时xxx毫秒”,单位是时间/操作数 @benchmarkmode(mode.averagetime)
//单位为纳秒 @outputtimeunit(timeunit.nanoseconds) public class proxyruntest { private proxyservice proxyservicecglib = (proxyservice)new cglibproxy().getproxy(proxyserviceimpl.class); private proxyservice proxyservicejdk = (proxyservice) new jdkinvocationhandler(new proxyserviceimpl()).getproxy(); private proxyservice proxyservicejavassist = (proxyservice) new javassistproxy().getproxy(proxyserviceimpl.class); public static void main(string args[]) throws exception{ options ops = new optionsbuilder().include(proxyruntest.class.getsimplename()) .forks(1).build(); new runner(ops).run(); } //方法注解,表示该方法是需要进行 benchmark 的对象。 @benchmark public void cglibproxyrun(){ proxyservicecglib.run(); } @benchmark public void jdkproxyrun(){ proxyservicejdk.run(); } @benchmark public void javassistproxyrun(){ proxyservicejavassist.run(); } }

 

测试结果

 

第一次测试
benchmark mode cnt score error units proxyruntest.cglibproxyrun avgt 20 9.918 ± 1.268 ns/op proxyruntest.javassistproxyrun avgt 20 34.226 ± 2.655 ns/op proxyruntest.jdkproxyrun avgt 20 5.225 ± 0.449 ns/op 第二次测试 benchmark mode cnt score error units proxyruntest.cglibproxyrun avgt 20 6.975 ± 0.629 ns/op proxyruntest.javassistproxyrun avgt 20 31.707 ± 0.885 ns/op proxyruntest.jdkproxyrun avgt 20 5.442 ± 0.514 ns/op 第三次测试 benchmark mode cnt score error units proxyruntest.cglibproxyrun avgt 20 8.079 ± 1.381 ns/op proxyruntest.javassistproxyrun avgt 20 33.916 ± 2.904 ns/op proxyruntest.jdkproxyrun avgt 20 5.947 ± 0.498 ns/op

 

从测试结果来看,javassist的代理对象调用执行性能最差。最好的是jdk方式。

总结

1.不管是代理创建还是方法调用执行,javassist方式的性能最好,jdk的方式最差。

2.对于实际使用过程中。代理对象一般只会创建一次,创建完成后缓存起来供后续使用,因此创建过程的性能对整体性能影响不大。jdk方式和cglib方式的方法调用执行性能差不多,实际可根据代理对象有无接口进行选择。