前言
有一天在看算法,发现书上的很多时间复杂度都是通过理论分析得来的,比如,二分搜索的时间复杂度是O(logn),是因为二分数据直至元素剩余1个的次数是logn。诚然,二分搜索的时间复杂度容易分析,但是有些算法的时间复杂度却是不容易分析出来的,对于这种情况,能够直接测量时间复杂度的工具就非常重要了。
jmh就是一组能够测量方法执行指标的工具,它的官网是JMH。
使用
在idea使用jmh做基准测试非常简单,就三步:
- 下载插件JMH Java Microbenchmark Harness
- 引入依赖库
- 编写测试类,加注解,运行
依赖库
// https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core
implementation 'org.openjdk.jmh:jmh-core:1.34'
// https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess
annotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.34'
测试类
public class Sleep {
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(1)
@Measurement(iterations = 3)
@Warmup(iterations = 1)
public void sleep1() throws InterruptedException {
sleep(1);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(1)
@Measurement(iterations = 3)
@Warmup(iterations = 1)
public void sleep10() throws InterruptedException {
sleep(10);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(1)
@Measurement(iterations = 3)
@Warmup(iterations = 1)
public void sleep100() throws InterruptedException {
sleep(100);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(1)
@Measurement(iterations = 3)
@Warmup(iterations = 1)
public void sleep1000() throws InterruptedException {
sleep(1000);
}
private void sleep(int n) throws InterruptedException {
for (int i = 0; i < n; i++) {
TimeUnit.MILLISECONDS.sleep(1);
}
}
}
以下是运行基准测试的结果:
Benchmark Mode Cnt Score Error Units
Sleep.sleep1 avgt 3 1.523 ± 0.060 ms/op
Sleep.sleep10 avgt 3 15.247 ± 0.336 ms/op
Sleep.sleep100 avgt 3 151.617 ± 6.507 ms/op
Sleep.sleep1000 avgt 3 1527.339 ± 89.302 ms/op
分析
取出数据,绘制折线图(文字忘的快,图记得久些):
可以看到,随着n的不断增大,消耗的时间呈线性增大,所以初步判定sleep
方法的时间复杂度是O(n)。
基准测试的入口
入口是org.openjdk.jmh.Main.main(..)
方法,入参是要进行基微测试的类。
注解说明
基准测试的参数默认值参见org.openjdk.jmh.runner.Defaults
。
@Benchmark
每个需要做基准测试的方法都需要打上这个注解。
@BenchmarkMode
基准测试的模式:
- Throughput:单位时间的操作数(ops/time)
- AverageTime:单位操作的平均时间(time/op)
- SampleTime:对每个操作的时间进行采样
- SingleShotTime:测量单个操作的时间
这里的
操作
是指调用一次@Benchmark所注释的方法。
@OutputTimeUnit
指定测量的时间单位。
@Fork
指定进程数
@Threads
指定线程数
@Warmup
指定预热次数
@Measurement
指定测量次数
@State/@Setup/@TearDown
@Setup和@TearDown需要在@State注释的类中使用。