JMH を使う理由
素朴なベンチマークの誤りを避けます。
「JMH を使う理由」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
JMH を使う理由
Java Microbenchmark Harness(JMH) は、Java コードの小さな部分のパフォーマンスを測定するための標準ツールです。JVM は高度な最適化を行う実行環境であるため、自分で作成したタイミング計測ループでは、ほとんどの場合、正しい数値が得られません。
JMH はこうした落とし穴を取り除くために存在します。
素朴なベンチマーク
典型的な最初の試みでは、ループを System.nanoTime() で囲みます。一見もっともらしく見えますが、マイクロベンチマークとしては大きな問題があります。
public class Main {
static int compute(int n) { return n * n + 7; }
public static void main(String[] args) {
long start = System.nanoTime();
int sink = 0;
for (int i = 0; i < 1_000_000; i++) sink = compute(i);
long elapsed = System.nanoTime() - start;
System.out.println("ns: " + elapsed + " sink=" + sink);
}
}問題 1: JIT ウォームアップ
JVM はインタープリタモードで起動し、何千回も呼び出されたホットメソッドだけをネイティブコードにコンパイルします。素朴なベンチマークでは、遅いインタープリタ実行の段階と速いコンパイル済み実行の段階が混在して計測されるため、平均値に意味がなくなります。
JMH は、専用のウォームアップ段階を設け、その結果を破棄することでこの問題を解決します。
問題 2: デッドコード除去
計算した結果がまったく使われない場合、JIT は計算そのものを完全に削除することがあります。その場合、ベンチマークが計測しているのは空のループです。
JMH は、戻り値の消費と Blackhole を提供して、この問題を防ぎます。
問題 3: 定数畳み込み
入力がコンパイル時定数の場合、JIT は答えを一度だけ計算し、それを再利用します。次の例では、コンパイラがループ全体を 1 つの定数に置き換える可能性があります。
JMH は @State オブジェクトを使用して、入力をオプティマイザーから不透明にします。
public class Main {
public static void main(String[] args) {
// 2 * 21 is constant; the JIT folds it to 42
int result = 2 * 21;
System.out.println(result);
}
}問題 4: ループ最適化
JIT はループを展開し、不変コードをループの外へ移動し、処理をベクトル化します。手書きのループを使うと、意図した処理ではなく、こうした最適化を計測することになります。
JMH はループを、反復回数を自ら管理する、慎重に制御された反復処理に置き換えます。
問題 5: オンスタック置換
main 内の長時間実行されるループは、実行途中にコンパイルされることがあります(オンスタック置換)。その場合、通常の方法としてコンパイルされたメソッドとは異なるパフォーマンス特性が生じます。これにより、結果が予測できない形で偏ります。
JMH が提供するもの
- 独立して実行され、結果が破棄されるウォームアップ反復。
- 統計情報を伴う複数の測定反復。
- プロファイルの汚染を避けるための、新しい JVM へのフォーク。
- デッドコード除去を防ぐBlackholeと結果の消費。
- 定数畳み込みを防ぐ@Stateオブジェクト。
実行方法
JMH は別の依存関係(org.openjdk.jmh)であり、通常はアノテーションプロセッサと、実行可能な JAR を生成する Maven/Gradle ビルドによって起動します。通常のコードのように、単なる main からベンチマークを実行することはありません。
統計情報が重要な理由
JMH は平均値だけでなく、反復とフォーク全体の誤差 / 信頼区間も報告します。42.0 +/- 1.3 ns/op という結果からは、中心となる値と、どの程度変動したかの両方が分かります。これは測定結果を信頼するために不可欠です。
JMH を使う場面
ホットパスの 2 つの実装を比較する場合、最適化を検証する場合、またはナノ秒からマイクロ秒単位の処理を測定する場合は、JMH を使用します。秒単位の大まかな処理(I/O、ネットワーク)では、通常、実時間の計測で十分です。
クイックチェック
JMH が必要な理由についての理解度を確認しましょう。
まとめ
マイクロベンチマークにハーネスが必要な理由を学びました。
- JVM はウォームアップします。インタープリタで実行されるコードは、多数の呼び出しの後に初めてコンパイル済みコードになります。
- デッドコード除去や定数畳み込みによって、処理が削除されたり事前計算されたりすることがあります。
- ループ最適化やオンスタック置換によって、手書きのループの結果が歪みます。
- JMH は、ウォームアップ、測定反復、フォーク、Blackhole、統計情報を追加します。
- ナノ秒単位のホットパスを測定するときに使用します。
よくある質問
「JMH を使う理由」レッスンは無料ですか?
はい。「JMH を使う理由」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「JMH を使う理由」で何を学びますか?
素朴なベンチマークの誤りを避けます。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「JMH を使う理由」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- JMH を使う理由
- ベンチマークの記述
- ウォームアップと反復
- デッドコード削除の回避