zoukankan      html  css  js  c++  java
  • JMH初体验

    Java 8

    JMH 1.19

    Eclipse Version: 2021-03 (4.19.0)

    ---

    JMH仓库

    https://github.com/openjdk/jmh

    https://mvnrepository.com/search?q=jmh

    最新版本:1.33 但没用起来,出现了OOM错误(后文解决了此问题)

    JMH is a Java harness for building, running, and analysing nano/micro/milli/macro benchmarks written in Java and other languages targetting the JVM.

    JMH 是 OpenJDK 团队开发的一款基准测试工具,一般用于代码的性能调优,精度甚至可以达到纳秒级别,适用于 java 以及其他基于 JVM 的语言。

    JMH是Java性能测试工具,主要是对工程中一些方法进行一些基准测试,支持的时间单位为:nano / micro / milli / macro

    根据参考文档1,添加了下面的测试程序:

    package jmh;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    import org.openjdk.jmh.annotations.Benchmark;
    import org.openjdk.jmh.annotations.BenchmarkMode;
    import org.openjdk.jmh.annotations.Level;
    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.runner.Runner;
    import org.openjdk.jmh.runner.RunnerException;
    import org.openjdk.jmh.runner.options.Options;
    import org.openjdk.jmh.runner.options.OptionsBuilder;
    
    /**
     * 使用JMH测试ArrayList、LinkedList的性能——add
     * @author ben
     * @date 2021-09-23 20:31:23 CST
     */
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    @State(Scope.Thread)
    public class Jmh1 {
    
    	// java.lang.OutOfMemoryError: Java heap space
    	// at java.util.Arrays.copyOf(Arrays.java:3181)
    	public final static String DATA = "DUMMY DATA";
    //	public final static String DATA = "D";
    //	public final static String DATA = "";
    	
    	private List<String> arrayList;
    	private List<String> linkedList;
    	
    	public static int cnt = 0;
    	
    	@Setup(Level.Iteration)
    	public void setUp() {
    		arrayList = new ArrayList<String>();
    		linkedList = new LinkedList<String>();
    	}
    	
    	@Benchmark
    	public List<String> arrayListAdd() {
    		cnt++;
    		try {
    			this.arrayList.add(DATA);
    		} catch (OutOfMemoryError e) {
    			// cnt=157704908, size=157704907
    			System.out.println("cnt=" + cnt + ", size=" + this.arrayList.size());
    			throw e;
    		}
    		return this.arrayList;
    	}
    	
    	@Benchmark
    	public List<String> linkedListAdd() {
    		this.linkedList.add(DATA);
    		return linkedList;
    	}
    	
    	public static void main(String[] args) throws RunnerException {
    		final Options opts = new OptionsBuilder()
    				.include(Jmh1.class.getSimpleName())
    				.forks(1)
    				.measurementIterations(10)
    				.warmupIterations(10)
    				.build();
    		new Runner(opts).run();
    	}
    
    }
    

    因为是maven项目,pom.xml中添加了下面的依赖包(前面截图中展示了):来自博客园

    <!-- JMH -->
    	<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
    	<dependency>
    	    <groupId>org.openjdk.jmh</groupId>
    	    <artifactId>jmh-core</artifactId>
    	    <version>1.19</version>
    	</dependency>
    	<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
    	<dependency>
    	    <groupId>org.openjdk.jmh</groupId>
    	    <artifactId>jmh-generator-annprocess</artifactId>
    	    <version>1.19</version>
            <!-- provided, compile 都可以 -->
    	    <scope>provided</scope>
    	</dependency>

    在Eclipse中启动,出现下面的错误:java.lang.RuntimeException: ERROR: Unable to find the resource: /META-INF/BenchmarkList

    根据 参考文档2 解决了问题:Eclipse安装插件 m2e-apt 1.5.3,并做配置

    来自博客园

    不过,安装插件并配置后,对其它项目的执行有什么影响吗?还需研究下,TODO

    上面使用的 JMH版本是 1.19,运行main函数:此时,笔记本的风扇呼呼地响,CPU使用率不用说,直接100%!/心疼电脑

    测试结果
    # JMH version: 1.19
    # VM version: JDK 1.8.0_202, VM 25.202-b08
    # VM invoker: D:Program FilesJavajdk1.8.0_202jreinjava.exe
    # VM options: -Dfile.encoding=UTF-8
    # Warmup: 10 iterations, 1 s each
    # Measurement: 10 iterations, 1 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Average time, time/op
    # Benchmark: jmh.Jmh1.arrayListAdd
    
    # Run progress: 0.00% complete, ETA 00:00:40
    # Fork: 1 of 1
    # Warmup Iteration   1: 0.015 us/op
    # Warmup Iteration   2: 0.024 us/op
    # Warmup Iteration   3: 0.015 us/op
    # Warmup Iteration   4: 0.016 us/op
    # Warmup Iteration   5: 0.017 us/op
    # Warmup Iteration   6: 0.012 us/op
    # Warmup Iteration   7: 0.021 us/op
    # Warmup Iteration   8: 0.010 us/op
    # Warmup Iteration   9: 0.010 us/op
    # Warmup Iteration  10: 0.012 us/op
    Iteration   1: 0.011 us/op
    Iteration   2: 0.020 us/op
    Iteration   3: 0.016 us/op
    Iteration   4: 0.027 us/op
    Iteration   5: 0.020 us/op
    Iteration   6: 0.013 us/op
    Iteration   7: 0.023 us/op
    Iteration   8: 0.017 us/op
    Iteration   9: 0.011 us/op
    Iteration  10: 0.020 us/op
    
    
    Result "jmh.Jmh1.arrayListAdd":
      0.018 ±(99.9%) 0.008 us/op [Average]
      (min, avg, max) = (0.011, 0.018, 0.027), stdev = 0.005
      CI (99.9%): [0.010, 0.025] (assumes normal distribution)
    
    
    # JMH version: 1.19
    # VM version: JDK 1.8.0_202, VM 25.202-b08
    # VM invoker: D:Program FilesJavajdk1.8.0_202jreinjava.exe
    # VM options: -Dfile.encoding=UTF-8
    # Warmup: 10 iterations, 1 s each
    # Measurement: 10 iterations, 1 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Average time, time/op
    # Benchmark: jmh.Jmh1.linkedListAdd
    
    # Run progress: 50.00% complete, ETA 00:00:23
    # Fork: 1 of 1
    # Warmup Iteration   1: 0.458 us/op
    # Warmup Iteration   2: 0.513 us/op
    # Warmup Iteration   3: 0.115 us/op
    # Warmup Iteration   4: 0.268 us/op
    # Warmup Iteration   5: 0.113 us/op
    # Warmup Iteration   6: 0.275 us/op
    # Warmup Iteration   7: 0.271 us/op
    # Warmup Iteration   8: 0.279 us/op
    # Warmup Iteration   9: 0.099 us/op
    # Warmup Iteration  10: 0.279 us/op
    Iteration   1: 0.097 us/op
    Iteration   2: 0.269 us/op
    Iteration   3: 0.095 us/op
    Iteration   4: 0.249 us/op
    Iteration   5: 0.095 us/op
    Iteration   6: 0.258 us/op
    Iteration   7: 0.093 us/op
    Iteration   8: 0.256 us/op
    Iteration   9: 0.092 us/op
    Iteration  10: 0.260 us/op
    
    
    Result "jmh.Jmh1.linkedListAdd":
      0.176 ±(99.9%) 0.131 us/op [Average]
      (min, avg, max) = (0.092, 0.176, 0.269), stdev = 0.087
      CI (99.9%): [0.046, 0.307] (assumes normal distribution)
    
    
    # Run complete. Total time: 00:01:18
    
    Benchmark           Mode  Cnt  Score   Error  Units
    Jmh1.arrayListAdd   avgt   10  0.018 ± 0.008  us/op
    Jmh1.linkedListAdd  avgt   10  0.176 ± 0.131  us/op
    

    最后两行展示如下:说明 ArrayList 的add的性能更好。

    # Run complete. Total time: 00:01:18
    
    Benchmark           Mode  Cnt  Score   Error  Units
    Jmh1.arrayListAdd   avgt   10  0.018 ± 0.008  us/op
    Jmh1.linkedListAdd  avgt   10  0.176 ± 0.131  us/op

    测试中、测试后的JVM进程对比:测试中多了 Jmh1、ForkedMain两个进程

    正常测试完毕,接下来,使用最新版的 JMH 1.33进行测试——修改pom.xml文件。

    执行测试:出现异常 java.lang.OutOfMemoryError: Java heap space,发生在 this.arrayList.add(DATA) 时。

    # Warmup Iteration   1: <failure>
    
    java.lang.OutOfMemoryError: Java heap space
    	at java.util.Arrays.copyOf(Arrays.java:3181)
    	at java.util.ArrayList.grow(ArrayList.java:265)

    注,上面是 没有捕获异常时的输出

    下面是本文展示的 捕获异常时的输出:第一个warmup居然执行了一亿五千多万次add!然后就发生了一次。

    这种情况下,LinkedList的测试是正常的,但ArrayList是没有测试结果的。

    最新版本1.33为什么会这样呢?怎么解决?TODO

    1.19版本执行时,ArrayList执行了多少次呢?输出每次++后的cnt:才八万多!和1.33的亿级完全没法比啊!难怪1.33会OOM

    ---210923 2209---

    解决1.33的OOM问题:来自博客园

    检查@Benchmark注解所在包下的注解,原来还有 @Warmup、@Measurement ,按照参考文档1的说法,这两个也是用来控制测试过程的,可以添加到类和方法上。

    在main方法中,使用 OptionsBuilder 来建立Options对象,发现其下有多个 warmup、measurement开头的方法,是否使用这些方法可以来避免OOM呢?可以的。

    下面的调用解决了问题:增加了一个 warmupTime、measurementTime 的调用,限定每次 预热、测试 在1秒以内——这样就不会调用add超亿次了吧(我的电脑太好、太快了?)

    	public static void main(String[] args) throws RunnerException {
    		final Options opts = new OptionsBuilder()
    				.include(Jmh1.class.getSimpleName())
    				.forks(1)
    				// 预热:使代码的执行经历过了类的早起优化、JVM运行期编译、JIT优化
    				.warmupIterations(10)
    				.warmupTime(TimeValue.seconds(1L))
    				// 真正的度量操作
    				.measurementIterations(10)
    				.measurementTime(TimeValue.seconds(1L))
    				.build();
    		new Runner(opts).run();
    	}

    更改后的测试结果:使用JMH 1.33成功进行了测试。来自博客园

    注,前两行 Warmup、Measurement 后面的 1 s each——正是我们配置的,1.19版本也是这个值,而1.33版本时,这个值是 10 s each——难怪会超亿!

    注,也可以使用前面提到的两个注解解决问题。

    ...
    # Warmup: 10 iterations, 1 s each
    # Measurement: 10 iterations, 1 s each
    # Timeout: 10 min per iteration
    ...
    # Run complete. Total time: 00:01:19
    
    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
    Jmh1.arrayListAdd   avgt   10  0.017 ± 0.012  us/op
    Jmh1.linkedListAdd  avgt   10  0.170 ± 0.126  us/op

    1.33版本的日志:

    ...
    # Warmup: 10 iterations, 10 s each
    # Measurement: 10 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Average time, time/op
    # Benchmark: jmh.Jmh1.arrayListAdd
    
    # Run progress: 0.00% complete, ETA 00:06:40
    # Fork: 1 of 1
    # Warmup Iteration   1: <failure>
    
    java.lang.OutOfMemoryError: Java heap space
    	at java.util.Arrays.copyOf(Arrays.java:3181)
    	at java.util.ArrayList.grow(ArrayList.java:265)
    ...

    ---210924 0657---

    》》》全文完《《《

    参考文档

    1、《Java高并发编程详解 深入理解并发核心库》

    书,by 汪文君

    2、Eclipse Benchmark 基准测试 报错:ERROR: Unable to find the resource: /META-INF/BenchmarkList

    3、arrays中copyof_Java中:常见的几种内存溢出及解决方案,再遇到后就可以解决了...

    4、 

  • 相关阅读:
    多叉树
    PowerDesigner设置集锦(2)
    Delphi应用程序在命令行下带参数执行返回命令行提示的问题
    不允许在 '*******' 上使用扩展属性,或对象不存在
    仓库管理系统开发完成
    动态创建Frame窗体(根据类名,除T以外的字母)
    Access中的常用TSQL
    批量删除同类文件(带通配符)
    判断Access数据库中的表或查询是否存在的SQL
    序列化FastReport,重要提示少走弯路
  • 原文地址:https://www.cnblogs.com/luo630/p/15327760.html
Copyright © 2011-2022 走看看