zoukankan      html  css  js  c++  java
  • JMH简单使用

    简介

    JMH(Java Microbenchmark Harness)是用于代码微基准测试的工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。该工具是由 Oracle 内部实现 JIT 的大牛们编写的,他们应该比任何人都了解 JIT 以及 JVM 对于基准测试的影响。

    添加maven依赖

    <dependency>
          <groupId>org.openjdk.jmh</groupId>
          <artifactId>jmh-core</artifactId>
          <version>1.21</version>
    </dependency>
    <dependency>
          <groupId>org.openjdk.jmh</groupId>
          <artifactId>jmh-generator-annprocess</artifactId>
          <version>1.21</version>
    </dependency>
    

    因为我们今天要测试fastjson和jackson的性能,所以引入它们的依赖

    <!--fastjson依赖-->
    <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
          <version>1.2.56</version>
     </dependency>
    <!--jackson依赖-->
     <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.9.4</version>
    </dependency>
    <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
          <version>2.9.4</version>
    </dependency>
    

    例子

    @BenchmarkMode(Mode.AverageTime)
    @State(Scope.Thread)
    @Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS)
    @Fork(1)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    @Measurement(iterations = 5)
    public class Client {
    
      public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
            .build();
        new Runner(options).run();
      }
    
      @Benchmark
      public void testFastJsonSerialize(Blackhole blackhole) {
        List<User> userList = Arrays.asList(new User("lisi", "123"), new User("Tony", "456"));
        blackhole.consume(JSON.toJSONString(userList));
      }
    
      @Benchmark
      public void testJacksonSerialize(Blackhole blackhole) throws JsonProcessingException {
        List<User> userList = Arrays.asList(new User("lisi", "123"), new User("Tony", "456"));
        ObjectMapper objectMapper = new ObjectMapper();
        blackhole.consume(objectMapper.writeValueAsString(userList));
      }
    
      @AllArgsConstructor
      @NoArgsConstructor
      @Setter
      @Getter
      @ToString
      static class User {
    
        private String username;
        private String password;
      }
    }
    

    测试fastjson和jackson在序列化对象的功能上的性能对比,输出结果为

    # JMH version: 1.21
    # VM version: JDK 11, OpenJDK 64-Bit Server VM, 11+28
    # VM invoker: D:javajdkopenjdk-11injava.exe
    # VM options: -javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.1.3libidea_rt.jar=8662:C:Program FilesJetBrainsIntelliJ IDEA 2019.1.3in -Dfile.encoding=UTF-8
    # Warmup: 2 iterations, 1 s each
    # Measurement: 5 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Average time, time/op
    # Benchmark: com.imooc.sourcecode.java.jmh.test1.Client.testFastJsonSerialize
    
    # Run progress: 0.00% complete, ETA 00:01:44
    # Fork: 1 of 1
    # Warmup Iteration   1: 0.858 us/op
    # Warmup Iteration   2: 0.555 us/op
    Iteration   1: 0.560 us/op
    Iteration   2: 0.560 us/op
    Iteration   3: 0.558 us/op
    Iteration   4: 0.556 us/op
    Iteration   5: 0.563 us/op
    
    
    Result "com.imooc.sourcecode.java.jmh.test1.Client.testFastJsonSerialize":
      0.559 ±(99.9%) 0.010 us/op [Average]
      (min, avg, max) = (0.556, 0.559, 0.563), stdev = 0.003
      CI (99.9%): [0.549, 0.569] (assumes normal distribution)
    
    
    # JMH version: 1.21
    # VM version: JDK 11, OpenJDK 64-Bit Server VM, 11+28
    # VM invoker: D:javajdkopenjdk-11injava.exe
    # VM options: -javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.1.3libidea_rt.jar=8662:C:Program FilesJetBrainsIntelliJ IDEA 2019.1.3in -Dfile.encoding=UTF-8
    # Warmup: 2 iterations, 1 s each
    # Measurement: 5 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Average time, time/op
    # Benchmark: com.imooc.sourcecode.java.jmh.test1.Client.testJacksonSerialize
    
    # Run progress: 50.00% complete, ETA 00:00:53
    # Fork: 1 of 1
    # Warmup Iteration   1: 79.133 us/op
    # Warmup Iteration   2: 15.930 us/op
    Iteration   1: 6.734 us/op
    Iteration   2: 6.634 us/op
    Iteration   3: 6.662 us/op
    Iteration   4: 6.642 us/op
    Iteration   5: 6.714 us/op
    
    
    Result "com.imooc.sourcecode.java.jmh.test1.Client.testJacksonSerialize":
      6.677 ±(99.9%) 0.172 us/op [Average]
      (min, avg, max) = (6.634, 6.677, 6.734), stdev = 0.045
      CI (99.9%): [6.505, 6.849] (assumes normal distribution)
    
    
    # Run complete. Total time: 00:01:46
    
    REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
    why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
    experiments, perform baseline and negative tests that provide experimental control, make sure
    the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
    Do not assume the numbers tell you what you want them to tell.
    
    Benchmark                     Mode  Cnt  Score   Error  Units
    Client.testFastJsonSerialize  avgt    5  0.559 ± 0.010  us/op
    Client.testJacksonSerialize   avgt    5  6.677 ± 0.172  us/op
    

    可以看到fastjson确实比jackson快,fastjson平均一次0.5us(微妙), jackson平均一次6us(微妙)。

    注解分析

    @Benchmark

    此注解添加在方法上,表示该方法要进行基准测试

    @BenchmarkMode

    基准测试的类型,默认Throughput

    • Throughput:整体吞吐量,每秒执行了多少次调用,单位为 ops/time
    • AverageTime:用的平均时间,每次操作的平均时间,单位为 time/op
    • SampleTime:随机取样,最后输出取样结果的分布
    • SingleShotTime:只运行一次,往往同时把 Warmup 次数设为 0,用于测试冷启动时的性能
    • All:上面的所有模式都执行一次

    @State

    表示一个对象的作用范围,默认Scope.Thread

    • Scope.Benchmark:所有测试线程共享一个实例,测试有状态实例在多线程共享下的性能
    • Scope.Group:同一个线程在同一个 group 里共享实例
    • Scope.Thread:每个测试线程分配一个实例

    @Warmup

    程序预热相关配置,因为JVM中JIT(即时编译)的存在,某个函数多次执行之后,会被编译成机器码,提高执行速度,为了更接近真实情况,需要提前预热,让JIT充分工作。

    • iterations:预热的次数,默认5
    • time:每次预热的时间,默认10s
    • timeUnit:时间的单位,默认秒
    • batchSize:批处理大小,每次操作调用几次方法,默认1次

    @Measurement

    测试基准测试相关配置,参数和@Warmup相同

    @Fork

    进程数量,默认5

    @OutputTimeUnit

    统计结果的时间单位,一般为秒,微妙,纳秒

    使用陷阱

    在使用JMH的过程中,会存在一些陷阱,如编译器进行的优化常量折叠,

    String str = "A" + "B";
    

    编译器会直接优化成

    String str = "AB";
    

    JIT的优化死码消除

    String str = "";
    for (int i = 0; i < 10; i++) {
      str += i;
    }
    

    JVM认为str没有被使用,整段代码都会被优化掉。所以我们在测试的过程中要避免编译器优化和JIT优化

    • 测试方法不要返回void
    • 返回void情况,使用Blackhole.consume()消除JIT优化
  • 相关阅读:
    电商项目剖析专题【延后。。】
    中间件专题(RabbitMQ+Kafka+MongoDB)
    ECMAScript + TypeScript + Nodejs 专题
    第七节:IDEA配置Maven、创建Maven项目、常用指令、tomcat7插件的配置 及 pom.xml详解
    第六节:Maven简介、安装配置、仓库分类与配置、工程介绍
    第八节:前端UI大换血(动态登录页、三套皮肤、菜单伸缩、弹框页面、基础布局页面等等)
    第五节:使用TortoiseGit管理Git操作 、IDEA集成Git、使用SSH协议传数据
    第四节:Git的概述、代码托管平台、常用指令总结、实际案例模拟
    第二节:IDEA的入门用法(快捷键、创建项目、调试模式、Jar包等等)
    第三节:IDEA创建Web项目、Tomcat下载配置及与IDEA的集成
  • 原文地址:https://www.cnblogs.com/strongmore/p/14018794.html
Copyright © 2011-2022 走看看