当我们编写一段Java代码之后,如果想知道代码性能如何,就需要进行一些快速的性能测试。
当我们实现一个需求,面临2种及以上的方案,选择一种性能更好的方案时,也需要进行一些快速的性能测试。
在之前的实践中,我一开始的测试代码通常是这样的:
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
doSomething();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
再到后来,变成这种:
public static void main(String[] args) {
def test = {
doSomething()
}
new FunQpsConcurrent(test, "Demo").start();
}
但最近又有了新的工具JMH(Java Microbenchmark Harness)是用于代码微基准测试的工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。该工具是由 Oracle 内部实现 JIT 的大牛们编写的,他们应该比任何人都了解 JIT 以及 JVM 对于基准测试的影响。
真可谓相见恨晚,上手也是非常迅速的,建议学习时间2小时,顺便看看官方GitHub仓库里面的错误示范,地址:https://github.com/lexburner/JMH-samples。
PS:
下面我分享一下我用JMH测试System.currentTimeMillis();和System.nanoTime();两个方法的性能。外行大胆猜测,第二个方法应该性能比较差。原因是第二个方法获取到纳秒时间戳,应该会更消耗性能。
这是我的测试用例:
package com.funtest.groovytest;
import com.funtester.frame.SourceCode;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS)
@Threads(2)
@Fork(1)
@State(value = Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JmhT extends SourceCode {
@Param(value = {"10", "20", "50"})
private int length;
@Benchmark
public void mill() {
System.currentTimeMillis();
}
@Benchmark
public void nano() {
System.nanoTime();
}
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(JmhT.class.getSimpleName())
.result("result.json")
.resultFormat(ResultFormatType.JSON)
.forks(1)
.threads(20)
.warmupIterations(2)
.warmupBatchSize(1)
.measurementIterations(2)
.measurementBatchSize(2)
.build();
new Runner(options).run();
}
}
最后的main方法是为了生产result.json做可视化用的,如果单纯想知道性能差异,可以直接使用Intellij插件完成。用例中的length完全不用,只是想展示一下参数化的用法。
文字版测试结果如下:
Benchmark (length) Mode Cnt Score Error Units
JmhT.mill 10 thrpt 2 0.015 ops/ns
JmhT.mill 20 thrpt 2 0.015 ops/ns
JmhT.mill 50 thrpt 2 0.016 ops/ns
JmhT.nano 10 thrpt 2 0.091 ops/ns
JmhT.nano 20 thrpt 2 0.088 ops/ns
JmhT.nano 50 thrpt 2 0.084 ops/ns
总体看就是System.nanoTime();性能要远远优于System.currentTimeMillis();,这是不是有点意外。