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进行基准测试是确保代码高效运行的关键步骤。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/625591.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode面试经典150题刷题记录 —— 二叉树层次遍历篇

Leetcod面试经典150题刷题记录-系列Leetcod面试经典150题刷题记录——数组 / 字符串篇Leetcod面试经典150题刷题记录 —— 双指针篇Leetcod面试经典150题刷题记录 —— 矩阵篇Leetcod面试经典150题刷题记录 —— 滑动窗口篇Leetcod面试经典150题刷题记录 —— 哈希表篇Leetcod面…

Linux的基础命令学习

pwd - 显示当前工作目录的路径 cd - 切换工作目录&#xff0c;ls - 列出当前目录的文件和子目录 rm - 删除文件或目录 mkdir - 创建新目录 rm - 删除目录 nano/vi - 编辑文本文件&#xff0c;按Enter键进入 之后按i键就可以进入写入模式 之后输入文字以后按Esc键与:q就不保…

C# 图解教程 第5版 —— 第22章 命名空间和程序集

文章目录 22.1 引用其他程序集22.2 命名空间22.2.1 命名空间名称22.2.2 命名空间的补充22.2.3 命名空间跨文件伸展22.2.4 嵌套命名空间 22.3 using 指令22.3.1 using 命名空间指令22.3.2 using 别名指令22.3.3 using static 指令 22.4 程序集的结构22.5 程序集标识符22.6 强命名…

Camunda Asynchronous continuations

示例一 Service public class ExceptionService implements JavaDelegate {Overridepublic void execute(DelegateExecution execution) {System.out.println(1/0);} }ACT_RU_TASK &#xff1a;明明是UserTask2完成任务了&#xff0c;ServiceTask内部出错了&#xff0c;按说事务…

Spring Boot接口请求响应慢,超过10秒以上,如无法优化SQL或代码的情况下,建议写入数据库或缓存中,请求接口时从数据库或缓存中读取返回

举例 Override public Map<String, Object> getCockpitStaffAttendanceTask() {Map<String, Object> map new HashMap<>();int chuqin 0; //出勤int queqin 0; //缺勤int chidao 0; //迟到int zaotui 0; //早退//获取所有设备卡号 并且已经绑定了人Lis…

MS-DETR: Efficient DETR Training with Mixed Supervision论文学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2401.03989.pdf 代码地址&#xff08;中稿后开源&#xff09;&#xff1a;GitHub - Atten4Vis/MS-DETR: The official implementation for "MS-DETR: Efficient DETR Training with Mixed Supervision" 摘要 DETR 通过迭代…

C语言中对关键字和标识符的理解

1.关键字(keyword) 定义&#xff1a;被C语言赋予了特殊含义&#xff0c;用做专门用途的字符串&#xff08;或单词&#xff09;。 特点&#xff1a;全部关键字都是小写字母。 举例&#xff1a; int、return等已经被C语言定义好了。 传统的C语言&#xff08;ANSI C&#xff0…

2024.1.15

这周是考试周昂&#xff0c;下周有三门相对重要的考试&#xff0c;所以说会将更多的时间与精力投入到复习中去&#xff0c;相对而言的投入到代码中的时间就会变少平时就以复习以前写过的代码为主了&#xff0c;这一周是不打算开新坑的&#xff0c;一切新的学习暂停&#xff0c;…

微调您的Embedding模型以最大限度地提高RAG管道中的相关性检索

英文原文地址&#xff1a;https://betterprogramming.pub/fine-tuning-your-embedding-model-to-maximize-relevance-retrieval-in-rag-pipeline-2ea3fa231149 微调您的Embedding模型以最大限度地提高RAG管道中的相关性检索 微调嵌入前后的 NVIDIA SEC 10-K 文件分析 2023 年…

高效工作法:占位图片生成工具助力项目快速迭代

在现代设计和开发项目中&#xff0c;图片资源的重要性不言而喻。然而&#xff0c;项目中经常会遇到寻找合适图片、调整图片尺寸和格式等问题&#xff0c;这些问题不仅耗时耗力&#xff0c;还可能影响到项目的进度和质量。此时&#xff0c;占位图片生成工具应运而生&#xff0c;…

C++知识补充(一)

C知识补充 由于之前有系统的学过 C 语言&#xff0c;现在在学习 C 的过程中采用的是对比学习方式&#xff0c;C 语言中没有涉及到的会补充记录一下。 内联函数 内联函数(Inline Function)是 C 中一种特殊的函数&#xff0c;其定义直接在每个调用点展开&#xff0c;这意味着编…

Jmeter 测试脚本录制器-HTTP 代理服务器

Jmeter 测试脚本录制器-HTTP 代理服务器 Jmeter 配置代理服务器代理服务器获取请求地址示例图配置步骤 浏览器配置代理Google 浏览器插件配置代理windows 本地网络配置代理 启动录制&#xff0c;生成证书生成证书导入证书Jmeter 配置证书 浏览器点击页面&#xff0c;录制请求地…

Linux笔记:Linux中的文件系统权限

在Red Hat Enterprise Linux 或其他类似的Linux发行版中&#xff0c;全局umask设置通常在几个不同的系统级配置文件中定义。以下是一些可能设置umask的地方&#xff1a; &#xff08;1&#xff09;/etc/profile: 这是为系统上的所有用户设置全局环境变量和启动程序的地方。通…

【算法与数据结构】343、LeetCode整数拆分

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;博主做这道题的时候一直在思考&#xff0c;如何找到 k k k个正整数&#xff0c; k k k究竟为多少合适。…

【Delphi 基础知识 19】Assigned的用法

在Delphi中&#xff0c;Assigned 是一个用于检查指针是否已分配内存的函数。它通常用于检查对象或指针是否已经被分配内存&#xff0c;以避免在未分配内存的情况下引用或操作它。 以下是 Assigned 的一些用法示例&#xff1a; 检查对象是否已分配内存&#xff1a; varMyObject…

linux终端查看文件

ls 命令&#xff1a;ls 命令用于列出当前目录下的文件和子目录。默认情况下&#xff0c;它以字母顺序列出文件和目录的名称。例如&#xff0c;要列出当前目录下的所有文件和目录&#xff0c;可以运行以下命令&#xff1a; ls ls -l 命令&#xff1a;ls -l 命令以长格式显示文件…

Nacos 高级详解

一 、服务集群 1 需求 服务提供者搭建集群 服务调用者&#xff0c;依次显示集群中各服务的信息 2 搭建 1&#xff09;修改服务提供方的controller&#xff0c;打印服务端端口号 package com.czxy.controller;import org.springframework.web.bind.annotation.*;import …

数据结构学习 jz39 数组中出现次数超过一半的数字

关键词&#xff1a;排序 摩尔投票法 摩尔投票法没学过所以没有想到&#xff0c;其他的都自己想。 题目&#xff1a;库存管理 II 方法一&#xff1a; 思路&#xff1a; 排序然后取中间值。因为超过一半所以必定在中间值是我们要的结果。 复杂度计算&#xff1a; 时间复杂度…

vtk9.3 关于vtk --- 来源于官方网站

Visualization Toolkit&#xff08;VTK&#xff09;是一个强大的开源软件系统&#xff0c;提供了在3D计算机图形学、图像处理、建模、体积渲染和科学可视化方面的高级功能。它提供了线程和分布式内存并行处理&#xff0c;以实现可伸缩性和更好的性能。 VTK 是一个跨平台库&…

大数据Doris(五十六):SQL函数之地理位置函数

文章目录 SQL函数之地理位置函数 一、​​​​​​​ST_AsText(GEOMETRY geo)