Goの go test -bench
のようにJavaでベンチマークを簡単に取りたかったので調べた。
jmh が有名どころのようなので、こちらを使ってみる。
jmhはメソッドなど小さな単位でマイクロベンチマークを取ることができる。 小さなコードを単純に実行するとコードが大きいときには行われない JIT コンパイルなどの最適化がはたらくことで実際よりパフォーマンスが高く出ることがあるが、 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>
package com.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;
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.Setup;
import org.openjdk.jmh.annotations.State;
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 org.openjdk.jmh.runner.options.TimeValue;
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class Bench {
private static byte[] body;
public static void main(String args[]) throws RunnerException {
Options opt = new OptionsBuilder()
.include(Bench.class.getSimpleName())
.forks(1)
.warmupTime(TimeValue.seconds(1))
.warmupIterations(1)
.measurementTime(TimeValue.seconds(30))
.measurementIterations(1)
.addProfiler("stack")
.addProfiler("gc")
.build();
new Runner(opt).run();
}
// 最初の一回だけ実行する処理。teardownもある
@Setup
public void prepare() throws IOException {
String path = "/body.bin";
try (InputStream is = Bench.class.getResourceAsStream(path);
BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
body = new byte[is.available()];
is.read(body);
}
}
@Benchmark
@BenchmarkMode(Mode.All)
public static void test() throws InterruptedException {
// do something using body...
}
}
IDEであればこのmain関数を実行すればベンチマークの結果が出力される。
mavenで実行可能jarファイル(Fat Jar)を作成する ことで java -jar
で実行することもできる。
自分でMainを書かずにエントリポイントに org.openjdk.jmh.Main
を指定すればコマンドライン引数でオプションを設定できる