网站做app开发工具,土特产网站模板,网站建设的岗位要求,做销售用什么网站好在软件开发中#xff0c;性能优化是一个永恒的话题。为了确保代码在生产环境中运行得尽可能快#xff0c;开发者需要一种准确的方法来度量和比较不同代码片段的性能。Java Microbenchmark Harness#xff08;JMH#xff09;是一个专门为Java和其他基于JVM的语言设计的工具性能优化是一个永恒的话题。为了确保代码在生产环境中运行得尽可能快开发者需要一种准确的方法来度量和比较不同代码片段的性能。Java Microbenchmark HarnessJMH是一个专门为Java和其他基于JVM的语言设计的工具它允许开发者以高精度执行微基准测试。
1.JMH简介
JMH是一个用于编写可靠Java微基准测试的工具。它可以帮助开发者量化代码片段的执行时间这对于理解代码性能至关重要。通过JMH开发者可以比较不同算法或代码实现的性能从而做出基于数据的优化决策。
JMH的设计考虑了基准测试中的各种陷阱如JVM的热点优化、死码消除和垃圾收集暂停。它提供了一组注解和工具类使得编写、配置和运行基准测试变得简单而直观。
2.JMH核心特性
注解驱动JMH使用注解来标记基准测试方法和配置测试参数。这些注解提供了丰富的配置选项如测试模式吞吐量、平均时间等、预热迭代次数、测量迭代次数等。隔离测试为了确保测试结果的可重复性JMH会在单独的JVM进程中运行每个基准测试。这样可以避免测试之间的干扰并确保每个测试都在相同的初始条件下运行。预热和迭代JMH允许开发者指定预热迭代次数以使得JVM的热点优化在测量阶段之前生效。此外通过多次迭代测试JMH可以计算统计上显著的结果减少偶然误差。结果统计JMH会自动收集和分析测试结果提供有关吞吐量、平均执行时间等的详细信息。这些信息对于理解代码性能瓶颈和优化方向非常有价值。
三、使用JMH进行基准测试
使用JMH进行基准测试涉及几个步骤添加依赖、编写基准测试类、配置测试选项和运行测试。
添加JMH依赖
dependencies dependency groupIdorg.openjdk.jmh/groupId artifactIdjmh-core/artifactId version1.33/version /dependency dependency groupIdorg.openjdk.jmh/groupId artifactIdjmh-generator-annprocess/artifactId version1.33/version scopeprovided/scope /dependency
/dependencies编写基准测试类
创建一个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() { // 这里放置你想要基准测试的代码 }
}运行基准测试
build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-surefire-plugin/artifactId version2.22.2/version configuration skipTeststrue/skipTests !-- 禁用常规的Maven测试 -- /configuration /plugin plugin groupIdorg.openjdk.jmh/groupId artifactIdjmh-maven-plugin/artifactId version1.33/version !-- 使用你需要的版本 -- executions execution idrun-benchmarks/id phaseintegrate-test/phase goals goalrun/goal /goals /execution /executions /plugin /plugins
/build然后你可以通过Maven命令来运行基准测试
mvn clean integrate-test通过JMH命令行工具运行
mvn clean package
java -jar target/benchmarks.jar3.JMH注解
Benchmark
这是一个方法注解用于声明该方法是一个基准测试方法。被此注解标记的方法将被JMH用于重复执行以便进行性能测量。
State
这是一个类注解用于声明该类是一个“状态”类。状态类定义了基准测试的状态可以包含测试所需的实例变量。它有一个Scope参数用于指定状态实例的生命周期和共享范围。
Scope枚举值
Scope.Thread每个测试线程分配一个状态实例。Scope.Benchmark所有测试线程共享一个状态实例。Scope.Group每个线程组共享一个状态实例。
Setup
这是一个方法注解用于指定在基准测试方法执行之前运行的初始化方法。通常用于准备测试数据或初始化状态。
TearDown
这是一个方法注解用于指定在基准测试方法执行之后运行的清理方法。通常用于释放资源或进行后处理。
Param
这是一个字段注解用于指定基准测试的参数。可以为基准测试方法提供不同的输入值以便测试在不同条件下的性能。
OutputTimeUnit
这是一个类或方法注解用于指定基准测试结果的时间单位。它使用java.util.concurrent.TimeUnit中的标准时间单位。
BenchmarkMode
这是一个类或方法注解用于指定基准测试的模式。Mode枚举值包括Throughput吞吐量AverageTime平均时间SampleTime随机采样时间SingleShotTime单次执行时间All所有模式。
Warmup
这是一个类或方法注解用于配置预热迭代的次数。预热迭代用于使JVM的热点代码优化达到稳定状态以获得更准确的基准测试结果。
Measurement
这是一个类或方法注解用于配置实际测量迭代的次数。这些迭代将用于收集性能数据。
Fork
这是一个类注解用于指定基准测试的进程分叉次数。每个分叉将在单独的进程中运行基准测试以减少噪声和干扰。
Threads
这是一个方法注解用于指定执行基准测试的线程数。它允许模拟多线程环境下的性能。
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类其中包含两个基准测试方法stringConcatPlus和stringConcatStringBuilder。我们使用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进行基准测试是确保代码高效运行的关键步骤。