JMH フレームワークを利用して意味のあるテストを作成しようとする人は誰でも、JMH サンプル テスト ( http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java /org/openjdk/jmh/samples/ )。それらを調べていくと、デッド コードの削除 (JMHSample_08_DeadCode.java) に行き詰まりました。
抜粋:
private double x = Math.PI;
@Benchmark
public void baseline() {
// do nothing, this is a baseline
}
@Benchmark
public void measureWrong() {
// This is wrong: result is not used, and the entire computation is optimized out.
Math.log(x);
}
measureWrong() の測定値は、ベースライン テストとほぼ同じになります。Math.log() の戻り値は使用されないためです。したがって、HotSpot コンパイラはデッド コードを排除します。了解しましたが、コンパイラは Math.log() を削除できるとどのように判断できますか。
テストを詳しく見てみると、Math.log() がネイティブ メソッドであることがわかります。そして、ネイティブ呼び出しは OS に行き、対応するライブラリを実行します。右?これにより、戻り値が使用されず、io 操作が実行されない場合、ネイティブ呼び出しがコンパイラによって削除される可能性があるという仮定が導かれます。
OS のどこかに常駐し、Java ワールドからのネイティブ呼び出しを処理する lib が戻り値を提供せずに io 操作 (ロギングなど) を行うとしたらどうでしょうか。それらの指示は完全に消去されますか?
仮定を証明するために、単純な JMH テストとネイティブ コールを使用してシナリオを再構築しました。実行する 3 つの C ネイティブ ライブラリをコンパイルしました。
- 返す 42
- パラメータ追加
- 空のファイルの作成
JMH テスト (measureWrong() テストと同様) でそれらを呼び出したので、io 操作を実行しないものでさえも、どれも削除されていません。テスト結果により、私たちの仮定は確認できません。ネイティブ呼び出しを最適化することはできません。つまり、Math.log() とカスタム ネイティブ呼び出しは同じ基準を持っていません。彼らは同じようにネイティブではありません。おそらく、ネイティブの lib コードに誤りがあり、少なくともテスト 1 のネイティブ コールは削除されているはずです。これが正しい場合は、コードを共有します。
そのため、さらに検索した結果、Java コードが、アーキテクチャの非常に最適化されたコードに対応するものに置き換えられる、組み込み関数という用語を見つけました。java.lang.Math.log() には、そのような組み込みの実装があります。ネイティブとイントリンシックの間に何か関係はありますか? 上記のネイティブとイントリンシクスの関係の仮定が有効である場合、コンパイラはネイティブ呼び出しを排除するために次の手順を実行しますか?
- コンパイル時に、HotSpot は Math.log() の組み込み実装 (jdk?) の存在をチェックし、Math.log() をそのコードに置き換えます。
- その後、HotSpot がメソッドの戻り値を確認する 2 番目のチェックが行われます。この結果で、HotSpot は Math.log() 呼び出しを完全に排除することを決定します。