1前書き

このクイック記事はJMH(Java Microbenchmark Harness)に焦点を当てています。これは、今後のJava 9リリースでJVMの一部になる予定です。

簡単に言うと、JMHはJVMのウォームアップやコード最適化パスなどの処理を行い、ベンチマークをできる限り簡単にします。


2入門

はじめに、実際にJava 8を使い続け、依存関係を定義するだけです。

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.19</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.19</version>
</dependency>


JMH Core

およびhttps://search.maven.org/の最新バージョンclassic/#artifactdetails%7Corg.openjdk.jmh%7Cjmh-generator-annprocess%7C1.19%7Cjar[JMHアノテーションプロセッサ]はMaven Centralにあります。

次に、

@ Benchmark

アノテーションを使用して(任意のパブリッククラスで)簡単なベンチマークを作成します。

@Benchmark
public void init() {
   //Do nothing
}

次に、ベンチマークプロセスを開始するメインクラスを追加します。

public class BenchmarkRunner {
    public static void main(String[]args) throws Exception {
        org.openjdk.jmh.Main.main(args);
    }
}


BenchmarkRunner

を実行すると、おそらく無駄なベンチマークが実行されます。実行が完了すると、要約表が表示されます。

# Run complete. Total time: 00:06:45
Benchmark      Mode  Cnt Score            Error        Units
BenchMark.init thrpt 200 3099210741.962 ± 17510507.589 ops/s


3ベンチマークの種類

JMHはいくつかの可能なベンチマークをサポートします:

Throughput、


AverageTime、


SampleTime

、および

SingleShotTime

。これらは

@ BenchmarkMode

アノテーションで設定できます。

@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void init() {
   //Do nothing
}

結果の表には、(スループットの代わりに)平均時間メトリックが入ります。

# Run complete. Total time: 00:00:40
Benchmark Mode Cnt  Score Error Units
BenchMark.init avgt 20 ≈ 10−9 s/op


4ウォームアップと実行の設定


@ Fork

アノテーションを使用して、ベンチマークの実行方法を設定できます。たとえば、

value

パラメータはベンチマークを実行する回数を制御し、

warmup

パラメータはベンチマークを結果を収集する前に何回実行するかを制御します。 :

@Benchmark
@Fork(value = 1, warmups = 2)
@BenchmarkMode(Mode.Throughput)
public void init() {
   //Do nothing
}

これにより、JMHは2つのウォームアップフォークを実行し、結果を破棄してからリアルタイムベンチマークに進むように指示されます。

また、

@ Warmup

アノテーションを使用して、ウォームアップの反復回数を制御することもできます。たとえば、

@ Warmup(iterations = 5)

は、デフォルトの20回ではなく、5回のウォームアップ反復で十分であることをJMHに伝えます。


5状態

それでは、

State

を利用することで、ハッシュアルゴリズムのベンチマークを取るという、それほど簡単ではなく、より指示的なタスクを実行する方法を検討しましょう。

パスワードを数百回ハッシュすることで、パスワードデータベースに対する辞書攻撃からの保護を強化することにしたとします。


State

オブジェクトを使用してパフォーマンスへの影響を調べることができます。

@State(Scope.Benchmark)
public class ExecutionPlan {

    @Param({ "100", "200", "300", "500", "1000" })
    public int iterations;

    public Hasher murmur3;

    public String password = "4v3rys3kur3p455w0rd";

    @Setup(Level.Invocation)
    public void setUp() {
        murmur3 = Hashing.murmur3__128().newHasher();
    }
}

ベンチマークの方法は次のようになります。

@Fork(value = 1, warmups = 1)
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void benchMurmur3__128(ExecutionPlan plan) {

    for (int i = plan.iterations; i > 0; i--) {
        plan.murmur3.putString(plan.password, Charset.defaultCharset());
    }

    plan.murmur3.hash();
}

ここでは、ベンチマークメソッドに渡されると、フィールド

iterations

にJMHによって

@ Param

アノテーションから適切な値が入力されます。

@ Setup

アノテーション付きメソッドはベンチマークの各呼び出しの前に呼び出され、分離を確実にする新しい

Hasher

を作成します。

実行が終了すると、以下のような結果が得られます。

# Run complete. Total time: 00:06:47

Benchmark                   (iterations)   Mode  Cnt      Score      Error  Units
BenchMark.benchMurmur3__128           100  thrpt   20  92463.622 ± 1672.227  ops/s
BenchMark.benchMurmur3__128           200  thrpt   20  39737.532 ± 5294.200  ops/s
BenchMark.benchMurmur3__128           300  thrpt   20  30381.144 ±  614.500  ops/s
BenchMark.benchMurmur3__128           500  thrpt   20  18315.211 ±  222.534  ops/s
BenchMark.benchMurmur3__128          1000  thrpt   20   8960.008 ±  658.524  ops/s


5結論

このチュートリアルでは、Javaのマイクロベンチマークハーネスを中心に説明しました。

いつものように、コード例はhttps://github.com/eugenp/tutorials/tree/master/jmh[on GitHub]にあります。