Java-基准测试

这篇具有很好参考价值的文章主要介绍了Java-基准测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在软件开发中,性能优化是一个永恒的话题。为了确保代码在生产环境中运行得尽可能快,开发者需要一种准确的方法来度量和比较不同代码片段的性能。Java Microbenchmark Harness(JMH)是一个专门为Java和其他基于JVM的语言设计的工具,它允许开发者以高精度执行微基准测试。

1.JMH简介

JMH是一个用于编写可靠Java微基准测试的工具。它可以帮助开发者量化代码片段的执行时间,这对于理解代码性能至关重要。通过JMH,开发者可以比较不同算法或代码实现的性能,从而做出基于数据的优化决策。

JMH的设计考虑了基准测试中的各种陷阱,如JVM的热点优化、死码消除和垃圾收集暂停。它提供了一组注解和工具类,使得编写、配置和运行基准测试变得简单而直观。

2.JMH核心特性

  1. 注解驱动:JMH使用注解来标记基准测试方法和配置测试参数。这些注解提供了丰富的配置选项,如测试模式(吞吐量、平均时间等)、预热迭代次数、测量迭代次数等。
  2. 隔离测试:为了确保测试结果的可重复性,JMH会在单独的JVM进程中运行每个基准测试。这样可以避免测试之间的干扰,并确保每个测试都在相同的初始条件下运行。
  3. 预热和迭代:JMH允许开发者指定预热迭代次数,以使得JVM的热点优化在测量阶段之前生效。此外,通过多次迭代测试,JMH可以计算统计上显著的结果,减少偶然误差。
  4. 结果统计:JMH会自动收集和分析测试结果,提供有关吞吐量、平均执行时间等的详细信息。这些信息对于理解代码性能瓶颈和优化方向非常有价值。

三、使用JMH进行基准测试

使用JMH进行基准测试涉及几个步骤:添加依赖、编写基准测试类、配置测试选项和运行测试。

  1. 添加JMH依赖
<dependencies>  
  <dependency>  
    <groupId>org.openjdk.jmh</groupId>  
    <artifactId>jmh-core</artifactId>  
    <version>1.33</version> 
  </dependency>  
  <dependency>  
    <groupId>org.openjdk.jmh</groupId>  
    <artifactId>jmh-generator-annprocess</artifactId>  
    <version>1.33</version> 
    <scope>provided</scope>  
  </dependency>  
</dependencies>
  1. 编写基准测试类

创建一个Java类,并使用JMH提供的注解来标记基准测试方法。例如,使用@Benchmark注解来标记要进行性能测量的方法,使用@BenchmarkMode来指定测试模式(如Throughput表示吞吐量,AverageTime表示平均时间),以及使用@OutputTimeUnit来指定输出结果的时间单位。

import org.openjdk.jmh.annotations.Benchmark;  
import org.openjdk.jmh.annotations.BenchmarkMode;  
import org.openjdk.jmh.annotations.Mode;  
import org.openjdk.jmh.annotations.OutputTimeUnit;  
import java.util.concurrent.TimeUnit;  

@BenchmarkMode(Mode.AverageTime)  
@OutputTimeUnit(TimeUnit.NANOSECONDS)  
public class MyBenchmark {  

    @Benchmark  
    public void measure() {  
        // 这里放置你想要基准测试的代码  
    }  
}
  1. 运行基准测试
<build>  
  <plugins>  
    <plugin>  
      <groupId>org.apache.maven.plugins</groupId>  
      <artifactId>maven-surefire-plugin</artifactId>  
      <version>2.22.2</version>  
      <configuration>  
        <skipTests>true</skipTests> <!-- 禁用常规的Maven测试 -->  
      </configuration>  
    </plugin>  
    <plugin>  
      <groupId>org.openjdk.jmh</groupId>  
      <artifactId>jmh-maven-plugin</artifactId>  
      <version>1.33</version> <!-- 使用你需要的版本 -->  
      <executions>  
        <execution>  
          <id>run-benchmarks</id>  
          <phase>integrate-test</phase>  
          <goals>  
            <goal>run</goal>  
          </goals>  
        </execution>  
      </executions>  
    </plugin>  
  </plugins>  
</build>

然后,你可以通过Maven命令来运行基准测试:

mvn clean integrate-test

通过JMH命令行工具运行:

mvn clean package  
java -jar target/benchmarks.jar

3.JMH注解

  1. Benchmark:
  • 这是一个方法注解,用于声明该方法是一个基准测试方法。
  • 被此注解标记的方法将被JMH用于重复执行,以便进行性能测量。
  1. State:
  • 这是一个类注解,用于声明该类是一个“状态”类。
  • 状态类定义了基准测试的状态,可以包含测试所需的实例变量。
  • 它有一个Scope参数,用于指定状态实例的生命周期和共享范围。
  1. Scope枚举值:
  • Scope.Thread:每个测试线程分配一个状态实例。
  • Scope.Benchmark:所有测试线程共享一个状态实例。
  • Scope.Group:每个线程组共享一个状态实例。
  1. Setup :
  • 这是一个方法注解,用于指定在基准测试方法执行之前运行的初始化方法。
  • 通常用于准备测试数据或初始化状态。
  1. TearDown :
  • 这是一个方法注解,用于指定在基准测试方法执行之后运行的清理方法。
  • 通常用于释放资源或进行后处理。
  1. Param :
  • 这是一个字段注解,用于指定基准测试的参数。
  • 可以为基准测试方法提供不同的输入值,以便测试在不同条件下的性能。
  1. OutputTimeUnit :
  • 这是一个类或方法注解,用于指定基准测试结果的时间单位。
  • 它使用java.util.concurrent.TimeUnit中的标准时间单位。
  1. BenchmarkMode :
  • 这是一个类或方法注解,用于指定基准测试的模式。
  • Mode枚举值包括:Throughput(吞吐量),AverageTime(平均时间),SampleTime(随机采样时间),SingleShotTime(单次执行时间),All(所有模式)。
  1. Warmup :
  • 这是一个类或方法注解,用于配置预热迭代的次数。
  • 预热迭代用于使JVM的热点代码优化达到稳定状态,以获得更准确的基准测试结果。
  1. Measurement :
  • 这是一个类或方法注解,用于配置实际测量迭代的次数。
  • 这些迭代将用于收集性能数据。
  1. Fork :
  • 这是一个类注解,用于指定基准测试的进程分叉次数。
  • 每个分叉将在单独的进程中运行基准测试,以减少噪声和干扰。
  1. Threads :
  • 这是一个方法注解,用于指定执行基准测试的线程数。
  • 它允许模拟多线程环境下的性能。
  1. Group :
  • 这是一个类和方法注解,用于将多个基准测试方法组合成一个测试组。
  • 测试组内的方法将按照指定的顺序执行,并且共享相同的状态实例(当使用Scope.Group时)。

4.简单的基准测试

比较两种字符串拼接方法的性能:使用+操作符和使用StringBuilder。

import org.openjdk.jmh.annotations.Benchmark;  
import org.openjdk.jmh.annotations.BenchmarkMode;  
import org.openjdk.jmh.annotations.Mode;  
import org.openjdk.jmh.annotations.OutputTimeUnit;  
import org.openjdk.jmh.annotations.Scope;  
import org.openjdk.jmh.annotations.State;  
import org.openjdk.jmh.runner.Runner;  
import org.openjdk.jmh.runner.options.Options;  
import org.openjdk.jmh.runner.options.OptionsBuilder;  

import java.util.concurrent.TimeUnit;  

@State(Scope.Thread)  
public class StringConcatBenchmark {  

    private static final String A = "Hello, ";  
    private static final String B = "World!";  

    @Benchmark  
    @BenchmarkMode(Mode.AverageTime)  
    @OutputTimeUnit(TimeUnit.NANOSECONDS)  
    public String stringConcatPlus() {  
        return A + B;  
    }  

    @Benchmark  
    @BenchmarkMode(Mode.AverageTime)  
    @OutputTimeUnit(TimeUnit.NANOSECONDS)  
    public String stringConcatStringBuilder() {  
        StringBuilder sb = new StringBuilder();  
        sb.append(A);  
        sb.append(B);  
        return sb.toString();  
    }  

    public static void main(String[] args) throws Exception {  
        Options opt = new OptionsBuilder()  
        .include(StringConcatBenchmark.class.getSimpleName())  
        .warmupIterations(5)  
        .measurementIterations(10)  
        .forks(1)  
        .build();  

        new Runner(opt).run();  
    }  
}

这个例子中,我们定义了一个StringConcatBenchmark类,其中包含两个基准测试方法:stringConcatPlusstringConcatStringBuilder。我们使用@State(Scope.Thread)注解来指定每个测试线程有其独立的状态实例。
@BenchmarkMode(Mode.AverageTime)@OutputTimeUnit(TimeUnit.NANOSECONDS)注解分别指定我们想要测量的是平均时间,并且输出结果的时间单位为纳秒。main方法中,我们配置了基准测试的运行选项,并通过Runner类来执行基准测试。

5.参数化基准测试

import org.openjdk.jmh.annotations.Benchmark;  
import org.openjdk.jmh.annotations.BenchmarkMode;  
import org.openjdk.jmh.annotations.Mode;  
import org.openjdk.jmh.annotations.OutputTimeUnit;  
import org.openjdk.jmh.annotations.Param;  
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.options.Options;  
import org.openjdk.jmh.runner.options.OptionsBuilder;  
  
import java.util.Arrays;  
import java.util.Random;  
import java.util.concurrent.TimeUnit;  
  
@State(Scope.Thread)  
public class ArraySortBenchmark {  
  
    @Param({"100", "1000", "10000"})  
    private int arraySize;  
  
    private Integer[] array;  
  
    @Setup  
    public void setup() {  
        array = new Integer[arraySize];  
        Random rand = new Random();  
        for (int i = 0; i < arraySize; i++) {  
            array[i] = rand.nextInt();  
        }  
    }  
  
    @Benchmark  
    @BenchmarkMode(Mode.AverageTime)  
    @OutputTimeUnit(TimeUnit.MICROSECONDS)  
    public void sortArrayTimSort() {  
        Arrays.sort(array);  
    }  
  
    @Benchmark  
    @BenchmarkMode(Mode.AverageTime)  
    @OutputTimeUnit(TimeUnit.MICROSECONDS)  
    public void sortArrayJava8ParallelSort() {  
        Arrays.parallelSort(array);  
    }  
  
    public static void main(String[] args) throws Exception {  
        Options opt = new OptionsBuilder()  
                .include(ArraySortBenchmark.class.getSimpleName())  
                .warmupIterations(5)  
                .measurementIterations(5)  
                .forks(1)  
                .build();  
  
        new Runner(opt).run();  
    }  
}

在这个例子中,我们使用@Param注解来定义了一个参数arraySize,它将在基准测试中取不同的值(100、1000、10000)。@Setup注解用于在执行基准测试之前进行一些初始化工作,在本例中是生成一个随机数组。
我们定义了两个基准测试方法:sortArrayTimSort使用Arrays.sort进行排序,而sortArrayJava8ParallelSort使用Arrays.parallelSort进行排序。我们将测量这两种方法对不同大小数组的平均排序时间。
main方法中,我们配置了基准测试的运行选项,并通过Runner类来执行基准测试。执行结果将包括每个数组大小和每种排序方法的平均执行时间。

6.结论

JMH是一个强大而灵活的工具,用于在Java和其他基于JVM的语言中进行微基准测试。通过掌握JMH的核心特性和最佳实践,开发者可以准确地度量和比较代码的性能,从而做出明智的优化决策。在性能关键的场景中,使用JMH进行基准测试是确保代码高效运行的关键步骤。文章来源地址https://www.toymoban.com/news/detail-791998.html

到了这里,关于Java-基准测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java 基础篇】Java多线程编程详解:线程创建、同步、线程池与性能优化

    Java是一门强大的编程语言,其中最引人注目的特性之一是多线程支持。多线程允许我们在同一程序中同时执行多个任务,这大大提高了应用程序的性能和响应能力。本文将深入介绍Java线程的基础知识,无论您是初学者还是有一些经验的开发人员,都将从中获益。 在计算机科

    2024年02月07日
    浏览(55)
  • Java多线程基础:虚拟线程与平台线程解析

    在这篇文章中,主要总结一些关于线程的概念,以及更近期的名为虚拟线程的特性。将了解平台线程和虚拟线程在性质上的区别,以及它们如何促进应用程序性能的改进 让我们以调用外部API或某些数据库交互的场景为例,看看线程执行的生命周期。 线程被创建并准备在内存

    2024年01月17日
    浏览(40)
  • Java线程池基础介绍

    1、线程池能够复用已经创建了的线程来执行任务,从而降低了频繁创建和销毁线程所带来的资源消耗; 2、任务创建完成时,不必等待线程的创建,能够立即执行,提高了任务响应的速度。   线程池中长期存活的线程数量。一般情况下,当线程处于空闲状态时也不会被销毁。

    2024年02月11日
    浏览(37)
  • 【JUC-1】java多线程线程基础知识

    继承Thread类. 实现Runable接口. 实现Callable接口. Runable/Callable接口的实现, 都是重写其中的run/call方法, 实现任务逻辑, 再由线程执行器(可以是Thread类,也可以是线程池)并发执行run/call的逻辑. 而Thread类中的包含start方法, 可以控制线程启动,执行任务. 当发生线程上下文切换时, 操作系

    2024年02月11日
    浏览(39)
  • 从零学Java 多线程(基础)

    1.1 多任务 现实生活中太多这样同时做多件事的例子了,看起来是多个任务同时都在做,其实本质上我们的大脑在同一时间依旧只能做一件事 1.2 多线程 原来是一条路,慢慢地因为车变多了,造成了道路堵塞,使通行效率变低。 为了提高使用的效率,能够充分利用道路,于是

    2024年01月19日
    浏览(44)
  • Java基础篇 | 多线程详解

    ✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: Java从入门到精通 ✨特色专栏: MySQL学习 🥭本文内容:Java基础篇 | 多线程详解 🖥️个人小站 :个人博客,欢迎大家访问 📚个人知识库: 知识

    2024年02月05日
    浏览(36)
  • JAVA基础-多线程入门(详解)

    目录 引言 一,线程概念 二,创建线程 2.1,继承Thread类,重写run方法 2.2,实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函 数的target 2.3,通过Callable和FutureTask创建线程 ( 线程有返回值)  三,线程状态  四,volatile和synchronized 4.1、volatilevola

    2024年02月15日
    浏览(41)
  • Java Web 实战 10 - 多线程基础之线程池

    大家好 , 这篇文章给大家带来的是多线程当中的线程池 , 使用线程池可以降低资源消耗 , 通过重复利用已创建的线程降低线程创建和销毁造成的消耗 . 还可以提高响应速度 , 当任务到达时,任务可以不需要等到线程创建就能立即执行 我们会从三个角度分析线程池 线程池是什

    2023年04月08日
    浏览(37)
  • java基础之线程安全问题以及线程安全集合类

    当多个线程同时访问同一个临界资源时,原子操作可能被破坏,会导致数据丢失, 就会触发线程安全问题 临界资源: 被多个线程同时访问的对象 原子操作: 线程访问临界资源的过程中不可更改和缺失的操作 互斥锁 每个对象都默认拥有互斥锁, 该锁默认不开启. 当开启互斥锁之后

    2024年01月18日
    浏览(54)
  • 【Java基础】线程同步类 CountDownLatch

    ​ 关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 正好今天项目中用到了CountDownLatch,那我们正好总结一下,通过本文你可以学到什么是CountDownLatch及其原理,

    2024年02月12日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包