13

重複の可能性:
JNI呼び出しが遅くなる原因は何ですか?

まず、この質問は、本当の必要性よりも好奇心から生まれたものだと言わせてください。

System.arraycopyたとえば、JavaからJNI呼び出しを実行する場合のオーバーヘッドと、配列を割り当てて要素をforループでコピーする場合のオーバーヘッドについて知りたいと思います。

オーバーヘッドが大きい場合は、システムコールを使用する代わりに、forループを使用するだけで補正される要素の大まかな「マジックナンバー」が存在する可能性があります。また、このオーバーヘッドの原因となるシステムコールには正確に何が関係していますか?スタックを呼び出しのコンテキストにプッシュする必要があると思います。これにはしばらく時間がかかる場合がありますが、プロセス全体の適切な説明を見つけることができません。

私の質問を明確にしましょう:

私は、arraycopyを使用することが、Javaで配列をコピーする最も速い方法であることを知っています。

そうは言っても、1つの要素のみの配列をコピーするためにそれを使用しているとしましょう。基盤となるOSを呼び出しているので、この呼び出しにはオーバーヘッドが必要です。このオーバーヘッドが何であるか、そして呼び出しの過程で何が起こるかを知りたいです。

arraycopyを使用して、私の質問の目的からあなたを誤解させてしまった場合は、申し訳ありません。JNI呼び出しのオーバーヘッドと、実際の呼び出しに何が関係しているかを知りたいです。

4

4 に答える 4

9

基礎となるOSを呼び出しているので...

システムコールがかなり高価であることは間違いありません。ただし、SysteminSystem.arraycopy()は少し誤称です。関連するシステム コールはありません。

...この呼び出しにはオーバーヘッドが必要です。このオーバーヘッドが何であるか、および呼び出しの過程で何が起こるかを知りたいです。

の定義を見るとSystem.arraycopy()、 と宣言されていnativeます。これは、メソッドが C++ で実装されていることを意味します。気が向いたら、JDK ソース コードを見て、C++ 関数を見つけることができます。OpenJDK 7 では、 と呼ばれJVM_ArrayCopy()、 に存在しhotspot/src/share/vm/prims/jvm.cppます。実装は驚くほど複雑ですが、本質的にはmemcpy().

arraycopy()通常のネイティブ関数として使用されている場合、それを呼び出すにはオーバーヘッドがあります。引数のチェックなどによってさらにオーバーヘッドが発生します。

ただし、JIT コンパイラーが を認識している可能性は非常に高いですSystem.arraycopy()。これは、コンパイラが C++ 関数を呼び出す代わりに、特別に細工されたマシン コードを生成して配列のコピーを実行する方法を知っていることを意味します。他の JVM については知りませんが、HotSpot にはSystem.arraycopy().

1つの要素のみの配列をコピーするためにそれを使用しているとしましょう

System.arraycopy()アレイが小さい場合は、手作りのループで打ち負かすことができるかもしれません。コンパイル時にサイズがわかっている場合は、ループを展開することもできるため、おそらくさらにうまくいくでしょう。ただし、最も狭い状況を除いて、これらすべてが実際に関連しているわけではありません。

于 2012-12-18T10:29:28.823 に答える
2

java.util.Arrays.copyOf の実装を見てください。

public static byte[] copyOf(byte[] original, int newLength) {
    byte[] copy = new byte[newLength];
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

System.arraycopy を使用するのは、これが最速の方法であるためです。

Java でネイティブ メソッドを呼び出すとコストがかかるかどうかということであれば、http: //www.javamex.com/tutorials/jni/overhead.shtml をご覧ください。

UPDATE質問は本当に興味深いので、いくつかのテストを行いました

        long t0 = System.currentTimeMillis();
        byte[] a = new byte[100];
        byte[] b = new byte[100];
        for(int i = 0; i < 10000000; i++) {
//            for(int j = 0; j < a.length; j++) {
//                a[j] = b[j];
//            }
            System.arraycopy(b, 0, a, 0, a.length);
        }
        System.out.println(System.currentTimeMillis() - t0);

これは、非常に短い配列 (< 10) では System.arraycopy がさらに遅くなる可能性があることを示しています。これはおそらくネイティブであるためですが、より大きな配列ではもはや問題ではなく、System.arraycopy の方がはるかに高速です。

于 2012-12-18T10:26:09.237 に答える
1

JNI呼び出しのオーバーヘッドと、実際の呼び出しに何が関係しているかを知りたいです。

このSystem.arraycopy()方法はかなり複雑です*。JITコンパイラがそれをインライン化する可能性は低いです(他の回答の1つが示唆しているように)。

一方、これは固有のネイティブメソッドであるため、JITコンパイラは最適化された呼び出しシーケンスを使用する可能性があります。つまり、これは通常のJNI呼び出しではない可能性があります。


*-System.arraycopy単純なメモリコピーではありません。配列の境界を超えて読み取りまたは書き込みを行わないように、引数をテストする必要があります。また、あるオブジェクト配列から別のオブジェクト配列にコピーする場合は、コピーされた各オブジェクトの実際のタイプを確認する必要がありますこれらすべてが、インラインで認識できるよりもはるかに多くのコードを追加します。

于 2012-12-18T10:51:33.070 に答える
0

あなたはそれを間違った方法で持っています。System.arraycopy() は、JVM によって提供される超高速のネイティブ実装です。

「オーバーヘッド」はありません - あるのは「利点」だけです

于 2012-12-18T10:23:30.407 に答える