JNI呼び出しが遅くなる原因は何ですか?


194

JavaでJNI呼び出しを行うと「境界を越える」のが遅いことを知っています。

しかし、それが遅くなる原因ですか?非常に遅くなるJNI呼び出しを行うときに、基になるjvm実装は何をしますか?


2
(+1)いい質問です。この件については、実際のベンチマークを行ったすべての人に調査結果を投稿することをお勧めします。
NPE、2011年

2
JNI呼び出しは、渡されたJavaオブジェクトをC(たとえば)が理解できるものに変換する必要があります。戻り値と同じです。型変換と呼び出しスタックのマーシャリングは、その良いチャンクです。
Dave Newton

デイブ、私は以前にそれについて理解して聞いたことがあります。しかし、変換は正確にはどのようなものですか?その「何か」は何ですか?詳細を探しています。
pdeva

直接ByteBufferを使用してJavaとCの間でデータを受け渡すと、オーバーヘッドが比較的低くなります。
Peter Lawrey、2011年

6
呼び出しには適切なCスタックフレームが必要であり、すべての有用なCPUレジスターをプッシュし(そしてそれらを元に戻します)、呼び出しにはフェンシングが必要です。また、インラインのような多くの最適化を防ぎます。また、スレッドは実行スタックロックを離れ(たとえば、ネイティブコードでバイアスロックが機能するようにするため)、それを取得する必要があります。
bestsss

回答:


174

まず、「遅い」というのは、数十ナノ秒かかる可能性があることについて話していることは注目に値します。ささいなネイティブメソッドについては、2010年にWindowsデスクトップで平均40 ns、Macデスクトップで平均11 nsの呼び出しを測定しました。あなたが多くの呼び出しをしているのでない限り、あなたは気づかないでしょう。

とはいえ、ネイティブメソッドの呼び出しは、通常のJavaメソッドの呼び出しより遅くなる可能性があります。原因は次のとおりです。

  • ネイティブメソッドは、JVMによってインライン化されません。また、この特定のマシン用にジャストインタイムでコンパイルされることもありません。すでにコンパイルされています。
  • Java配列は、ネイティブコードでアクセスするためにコピーされ、後でコピーされます。コストは、アレイのサイズに比例する場合があります。100,000アレイのJNI コピーを測定したところ、Windowsデスクトップでは平均で約75マイクロ秒、Macでは82マイクロ秒でした。幸い、GetPrimitiveArrayCriticalまたはNewDirectByteBufferを介して直接アクセスを取得できます。
  • メソッドにオブジェクトが渡された場合、またはコールバックを行う必要がある場合は、ネイティブメソッドが独自のJVMを呼び出す可能性があります。ネイティブコードからJavaフィールド、メソッド、および型にアクセスするには、リフレクションと同様のものが必要です。署名は文字列で指定され、JVMから照会されます。これは時間がかかりエラーが発生しやすくなります。
  • Java文字列はオブジェクトであり、長さを持ち、エンコードされています。文字列にアクセスまたは作成するには、O(n)コピーが必要な場合があります。

おそらく日付が付けられたいくつかの追加の議論は、スティーブウィルソンとジェフケッセルマンによる「Java "プラットフォームパフォーマンス:戦略と戦術」2000年のセクション「9.2:JNIコストの調査」にあります。下の@Philipのコメントで提供されているこのページの約3分の1です。

2009年のIBM developerWorksペーパー「Java Native Interfaceを使用するためのベストプラクティス」では、JNIによるパフォーマンスの落とし穴を回避するためのいくつかの提案を提供しています。


1
この回答は、一部のネイティブコードはJVMによってインライン化できると主張しています。
AH

5
その回答は、JNIを使​​用するのではなく、一部の標準ネイティブコードがJVMにインライン化されていることを示しています。上記の「ネイティブメソッド」とは、JNIを介して実装されるユーザー定義のネイティブメソッドの一般的なケースを指します。sun.misc.Unsafeへのポインタをありがとう。
アンディトーマス

このアプローチはすべてのJNI呼び出しに使用できると主張したくありませんでした。しかし、それはそこにいることを知って傷つけることはありません、純粋なバイトコードと純粋なJNIコードの間にいくつかの妥協点。おそらくこれは、いくつかの設計上の決定に影響を与えます。おそらく、このメカニズムは将来的に一般化されるでしょう。
AH

3
@AH、あなたはJNI付きの組み込みを間違えました。それらはかなり異なります。sun.misc.Unsafeと他の多くのようなものSystem.currentTimeMillis/nanoTimeはJVMによって「魔法」を介して処理されます。それらはJNIではなく、適切な.c / .hファイルがまったくないため、JVM impl自体は除外されます。あなたがJVMを書いたりハッキングしたりしない限り、このアプローチに従うことはできません。
bestss


25

でマークされたすべてのJavaメソッドnativeが「遅い」とは限らないことに言及する価値があります。それらのいくつかは、それらを非常に高速にする組み込み関数です。組み込みのものとそうでないものを確認するにはdo_intrinsicvmSymbols.hppを探します。


23

基本的に、JVMは各JNI呼び出しのCパラメータを解釈的に構築し、コードは最適化されません。

このホワイトペーパーでは、より多くの詳細について概説しています。

JNIとネイティブコードのベンチマークに興味がある場合、このプロジェクトにはベンチマークを実行するためのコードがあります。


2
リンク先の論文は、JNIの内部動作を説明する論文よりも、パフォーマンスベンチマークの論文のように見えます。
pdeva

@pdeva残念ながら、私が見つけた他のリソースはjava.sun.comにリンクされており、リンクはOracleの買収以来更新されていません。JNI内部の詳細を探しています。
dmck

13
この論文はJava 1.3に関するものです-かなり昔のことです。当時の問題はJava 7にも当てはまりますか?
AH
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.