web-dev-qa-db-ja.com

Sun.misc.Unsafeを使用してJNIなしでC関数を呼び出すことは可能ですか?

C/C++コードの一部は、関数ポインターの配列を備えたJNIメソッドを提供できます。しかし、配列のポインタが指している関数をスタックに呼び出す方法はありますか?Javaコード(JNIなどを使用せずに))?JNIはどういうわけかそのようなことをするので、そこにJNIはどのようにそれを行うのですか?Sun.misc.Unsafe経由ですか?そうでない場合でも、安全でない回避策を使用して、それを行うJVMコードを入手できますか?

もちろん、それを商業的に使用するつもりはありません。私はプロでもありません。コーディングをとても楽しんでいます。最近CUDAを勉強しているので、すべてを混ぜ合わせて実験できるかもしれないと思いましたが、JNI呼び出しのオーバーヘッドは、GPUでコードを高速化するという目的を損ないます。

23
FinnTheHuman

JNIはそんなに遅いですか?

JNIはすでに多くの最適化が行われているので、最初に試してみてください。しかし、確かに特定のオーバーヘッドがあります 詳細を参照

ネイティブ関数が単純で頻繁に呼び出される場合、このオーバーヘッドは重大になる可能性があります。 JDKには、Critical Nativesと呼ばれるプライベートAPIがあり、JNI機能の多くを必要としない関数を呼び出すオーバーヘッドを削減します。

重要なネイティブ

ネイティブメソッドがクリティカルネイティブになるには、次の条件を満たす必要があります。

  • staticおよび同期されていないである必要があります。
  • 引数の型はプリミティブまたはプリミティブ配列でなければなりません。
  • 実装はJNI関数を呼び出さないでください。つまり、Javaオブジェクトを割り当てたり、例外をスローしたりすることはできません。
  • 実行中にGCをブロックするため、長時間実行しないでください。

重要なネイティブの宣言は、通常のJNIメソッドのように見えますが、

  • JavaCritical_ではなくJava_で始まります。
  • 余分なJNIEnv*およびjclass引数はありません。
  • Java配列は、2つの引数で渡されます。1つ目は配列の長さで、2つ目は生の配列データへのポインターです。つまり、GetArrayElementsやその仲間を呼び出す必要はなく、直接配列ポインタをすぐに使用できます。

例えば。 JNIメソッド

JNIEXPORT jint JNICALL
Java_com_package_MyClass_nativeMethod(JNIEnv* env, jclass klass, jbyteArray array) {
    jboolean isCopy;
    jint length = (*env)->GetArrayLength(env, array);
    jbyte* buf = (*env)->GetByteArrayElements(env, array, &isCopy);
    jint result = process(buf, length);
    (*env)->ReleaseByteArrayElements(env, array, buf, JNI_ABORT);
    return result;    
}

になります

JNIEXPORT jint JNICALL
JavaCritical_com_package_MyClass_nativeMethod(jint length, jbyte* buf) {
    return process(buf, length);
}

重要なネイティブは、JDK7以降のHotSpotJVMでのみサポートされます。さらに、「重要な」バージョンは、コンパイルされたコードからのみ呼び出されます。したがって、これを正しく機能させるには、重要な実装と標準的な実装の両方が必要です。

この機能は、JDKの内部使用のために設計されました。公的な仕様などはありません。おそらくあなたが見つけるかもしれない唯一のドキュメントは JDK-7013347 へのコメントにあります。

基準

このベンチマーク は、ネイティブワークロードが非常に小さい場合、重要なネイティブが通常のJNIメソッドよりも数倍高速になる可能性があることを示しています。メソッドが長いほど、相対的なオーバーヘッドは小さくなります。

Performance of JNI calls


P.S。JDKには、JNIのより高速な代替手段として機能するNativeMethodHandlesを実装するための進行中の作業があります。ただし、JDK10より前に表示される可能性はほとんどありません。

  1. http://cr.openjdk.Java.net/~jrose/panama/native-call-primitive.html
  2. http://mail.openjdk.Java.net/pipermail/panama-dev/2015-December/000225.html
65
apangin

ここで言及する価値があるのは、 別の人気のあるオープンソースJVM にも同様の 文書化された がありますが、一部のネイティブメソッドのJNI呼び出しを高速化する一般的な方法ではありません。

Java Native Interface(JNI)へのより高速なネイティブ呼び出しは、@ FastNativeおよび@ CriticalNativeアノテーションこれらの組み込みARTランタイム最適化は、JNI遷移を高速化し、現在は非推奨の!bang JNIアノテーションを置き換えます。アノテーションは非ネイティブメソッドには影響せず、プラットフォームでのみ使用可能Javaブートクラスパスの言語コード(Playストアの更新なし)。

@ FastNativeアノテーションは、非静的メソッドをサポートします。メソッドがパラメータまたは戻り値としてジョブジェクトにアクセスする場合にこれを使用します。

@ CriticalNativeアノテーションは、次の制限付きで、ネイティブメソッドを実行するさらに高速な方法を提供します。

  • メソッドは静的である必要があります。パラメーター、戻り値、または暗黙のthisのオブジェクトは使用できません。
  • プリミティブ型のみがネイティブメソッドに渡されます。
  • ネイティブメソッドは、関数定義でJNIEnvおよびjclassパラメーターを使用しません。
  • メソッドは、動的JNIリンクに依存するのではなく、RegisterNativesに登録する必要があります。

@ FastNativeおよび@ CriticalNativeアノテーションは、ネイティブの実行中にガベージコレクションを無効にします方法。通常は高速ですが、一般的に制限のないメソッドを含む、実行時間の長いメソッドでは使用しないでください。

ガベージコレクションを一時停止すると、デッドロックが発生する可能性があります。ロックがローカルで解放されていない場合(つまり、マネージコードに戻る前)は、高速ネイティブ呼び出し中にロックを取得しないでください。 ARTは実行中のネイティブコードを一時停止と見なすため、これは通常のJNI呼び出しには適用されません。

@ FastNativeはネイティブメソッドのパフォーマンスを最大3倍向上させることができ、@ CriticalNative最大5倍。

このドキュメントは、Dalvik JVMでのネイティブ呼び出しを高速化するために使用された非推奨の!bang表記を参照しています。

3
Alex Cohn