- 1、概览
- 2、jmh 简介
- 3、jmh 应用demo
- 4、jmh 罕用设置介绍
- 5、注意事项
你的致力,终将成就无可替代的本人
未来的你肯定会感激当初拼命的本人
1、概览
在日常开发中,咱们往往须要优化咱们本人写的代码。优化后的代码,执行效率是否比之前的还高?具体高多少?这些都是须要去测量。
目前比拟支流的做法是应用 jmh 进行微基准测试。
2、jmh 简介
jmh 是 java 用于微基准测试工具套件。次要是基于办法层面的基准测试,精度可达纳秒级。由 oracle 实现 JIT 大牛编写而成。
在应用 jmh 之前,咱们往往会先通过各种工具(jvisualvm)找到热点代码, 而后再对热点代码应用 jmh 进行量化剖析。
3、jmh 应用demo
上面应用字符串拼接作为案例介绍
第一步:退出依赖
maven 中引入 jmh jar 包
<dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.0</version></dependency><dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.0</version> <scope>provided</scope></dependency>第二步:编写基准测试
接下来,创立测试类,来判断 + 还是 StringBuilder.append() 吞吐量更高
@BenchmarkMode(Mode.Throughput)@Warmup(iterations = 3)@Measurement(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)@Threads(1)@Fork(1)@State(Scope.Benchmark)@OutputTimeUnit(TimeUnit.MILLISECONDS)public class StringBenchmark { String a = "1"; String b = "2"; String c = "3"; @Benchmark public String builderBenchmark() { return new StringBuilder().append(a).append(b).append(c).toString(); } @Benchmark public String connectionBenchmark() { return a + b + c; } public static void main(String[] args) throws RunnerException { Options options = new OptionsBuilder() .include(StringBenchmark.class.getSimpleName()) .build(); new Runner(options).run(); }}第三步:查看执行后果
# Warmup: 3 iterations, 1 s each# Measurement: 3 iterations, 5 s each# Threads: 1 thread, will synchronize iterations# Benchmark mode: Throughput, ops/time# Benchmark: com.csp.boot.jmh.StringBenchmark.builderBenchmark以上输入来自于咱们的配置。
第一行示意预热 3 次,每次 1 秒。
第二行示意运行 3 次,每次运行 5 秒。
第三行示意 1 个线程运行
第四行示意统计的数据纬度为吞吐量
# Run progress: 0.00% complete, ETA 00:00:36# Fork: 1 of 1# Warmup Iteration 1: 27694.373 ops/ms# Warmup Iteration 2: 47351.819 ops/ms# Warmup Iteration 3: 60008.968 ops/msIteration 1: 65411.091 ops/msIteration 2: 64443.826 ops/msIteration 3: 65067.621 ops/ms# Fork 示意子过程。因为只配置了 1 个,所以只有一个过程执行后果。# Warmup Iteration 为预热的数据,不会被计入统计,咱们配置了 3 次预热,所以有 3 个后果。Iteration 办法执行的后果
Result: 64974.180 ±(99.9%) 8945.921 ops/ms [Average] Statistics: (min, avg, max) = (64443.826, 64974.180, 65411.091), stdev = 490.356 Confidence interval (99.9%): [56028.259, 73920.100]统计后果给出了屡次测量后的最小值、均值,最大值,以及标准差(stdev),置信区间。
Benchmark Mode Samples Score Score error UnitsbuilderBenchmark thrpt 3 64974.180 8945.921 ops/msconnectionBenchmark thrpt 3 63524.697 69103.252 ops/ms在最初,会给出 2 个基准测试的性能比照。
从下面后果来看,应用 + 和 StringBuilder.append() 吞吐量差不多,起因在于,+ 在编译时,会应用 StringBuilder.append() 追加字符。
4、jmh 罕用设置介绍
@BenchmarkModeBenchmarkMode为应用模式,可选值如下:Mode.Throughput:吞吐量模式AverageTime: 示意每次执行工夫SampleTime: 示意采样工夫SingleShotTime: 示意只运行一次,用于测试冷启动生产工夫All: 示意统计后面所有指标
@Warmup
配置预热次数,本例是 3
@Measurement
配置执行次数,本例是运行 5 秒,总共执行 3 次。如果是做性能测试,默认应用 1 秒即可
@Threads
配置同时执行多少个线程,默认值是Runtime.getRuntime().availableProcessors(),本例采纳 1
@Fork
启动多少个子过程别离测试每个被@Benchmark标识的办法,本例采纳 1
@OutputTimeUnit
统计后果的工夫单元,本例是TimeUnit.MILLISECONDS
@Benchmark
用于标识哪些办法须要被测试
@State
一般而言,性能测试,都会援用一些内部的对象,jmh要求必须设置内部变量的作用域。能够应用@State示意内部对象的作用域。@State作用于类上,被@State标识的对象是在Thread范畴内还是在Benchmark。如果是Thread,则会为每个线程,独自创建对象。如果是Benchmark则所有测试共享。
本例的内部变量为a b c,@State值为Benchmark。
@Setup、@TearDown
2 个注解,均作用于办法上。@Setup用于测试前的初始化工作;@TearDown用于回收某些资源
@Param
指定某项参数的多种状况,特地适宜用来测试一个函数在不同的参数输出的状况下的性能,只能作用在字段上,应用该注解必须定义@State注解。
5、注意事项
为了防止 JIT 优化。因而对于被测试方法,尽量把后果返回。例如以下这段代码,会因为 i 没有被应用,而间接不执行 for 循环
public void add() { int i = 12; for (int j = 0; j < 12; j++) { i += j; }}失常的代码如下
public int add() { int i = 12; for (int j = 0; j < 12; j++) { i += j; } return i;}