5

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 ネイティブ ライブラリをコンパイルしました。

  1. 返す 42
  2. パラメータ追加
  3. 空のファイルの作成

JMH テスト (measureWrong() テストと同様) でそれらを呼び出したので、io 操作を実行しないものでさえも、どれも削除されていません。テスト結果により、私たちの仮定は確認できません。ネイティブ呼び出しを最適化することはできません。つまり、Math.log() とカスタム ネイティブ呼び出しは同じ基準を持っていません。彼らは同じようにネイティブではありません。おそらく、ネイティブの lib コードに誤りがあり、少なくともテスト 1 のネイティブ コールは削除されているはずです。これが正しい場合は、コードを共有します。

そのため、さらに検索した結果、Java コードが、アーキテクチャの非常に最適化されたコードに対応するものに置き換えられる、組み込み関数という用語を見つけました。java.lang.Math.log() には、そのような組み込みの実装があります。ネイティブとイントリンシックの間に何か関係はありますか? 上記のネイティブとイントリンシクスの関係の仮定が有効である場合、コンパイラはネイティブ呼び出しを排除するために次の手順を実行しますか?

  • コンパイル時に、HotSpot は Math.log() の組み込み実装 (jdk?) の存在をチェックし、Math.log() をそのコードに置き換えます。
  • その後、HotSpot がメソッドの戻り値を確認する 2 番目のチェックが行われます。この結果で、HotSpot は Math.log() 呼び出しを完全に排除することを決定します。
4

1 に答える 1

9

テストを詳しく見てみると、Math.log() がネイティブ メソッドであることがわかります。そして、ネイティブ呼び出しは OS に行き、対応するライブラリを実行します。右?

ネイティブ コールは OS には送られず、JNI 経由でネイティブ ライブラリに送られます。それは最終的にOSに送られるか、ユーザーが提供するライブラリに送られる可能性があります。JDK のネイティブ メソッドの場合、一部のネイティブ呼び出しが組み込みとしてコンパイルされることも期待できます。

これにより、戻り値が使用されず、io 操作が実行されない場合、ネイティブ呼び出しがコンパイラによって削除される可能性があるという仮定が導かれます。

JVM は、任意のネイティブ呼び出しを調べて、どのような種類の副作用があるかどうかを判断しません。これは、ネイティブ呼び出しが実際にメソッド呼び出しとして行われることを意味します (アセンブリ レベルでは、どこかの外部コード、スタック上の別のフレームなどにジャンプします)。これは、JVM がそれらまたはそれらに依存する入力を排除できないことも意味します。

ネイティブ呼び出しを最適化することはできません。つまり、Math.log() とカスタム ネイティブ呼び出しは同じ基準を持っていません。

はい。

ネイティブとイントリンシックの間に何か関係はありますか?

一部のネイティブ JDK メソッドは組み込み関数です。ただし、通常の JDK メソッドも組み込み関数にすることができます。組み込みメソッドのセットも、JVM ごとに異なります。

上記のネイティブとイントリンシクスの関係の仮定が有効である場合、コンパイラはネイティブ呼び出しを排除するために次の手順を実行しますか?

Math.log 関数は、C2 コンパイラ IR (中間表現) で特別なノードに変換されます。このノードは、副作用がなく、その値が使用されないことがわかっているため、最適化して取り除くことができます。値が使用されている場合、JVM はこのノードに特化したマシン コードを発行することを認識します。

要約すると、組み込み関数は、JVM コンパイラに組み込まれた最適化されたメソッドの置き換えです。これらを使用して、任意のメソッド (ネイティブまたはその他) を次のように置き換えることができます。

  1. 特殊なアセンブリ コード
  2. 専用IRコード
  3. 内部 JVM メソッド呼び出し
  4. 上記の組み合わせ
于 2015-08-03T09:06:30.633 に答える