System.arraycopyがJavaでネイティブなのはなぜですか?


84

System.arraycopyがネイティブメソッドであることをJavaソースで見て驚いた。

もちろん、その理由はそれが速いからです。しかし、コードがそれをより速くするために採用できるネイティブのトリックは何ですか?

元の配列をループして、各ポインターを新しい配列にコピーしないのはなぜですか?確かに、これはそれほど遅くて面倒ではありませんか?

回答:


82

ネイティブコードでは、n個の個別のコピー操作ではなく、単一のmemcpy/memmoveで実行できます。パフォーマンスの違いはかなりのものです。


@Peter、ネイティブコード内で、Javaメモリモデルを使用できますか?(私はネイティブのマラキーをする理由がありませんでした)
ジェームズB

8
実際には、/arraycopyを使用して実装できるのはの一部のサブケースのみです。その他の場合、コピーされた要素ごとに実行時型チェックが必要です。memcpymemmove
スティーブンC

1
@スティーブンC、興味深い-それはなぜですか?
ペーテルTörök

3
@PéterTörök-オブジェクトがObject[]入力された場所からにコピーすることを検討StringしてくださいString[]java.sun.com/javase/6/docs/api/java/lang/の
Stephen C

3
Peter、Object []およびbyte [] + char []は最も頻繁にコピーされるものであり、明示的な型チェックを必要とするものはありません。コンパイラーは、必要な場合を除いてチェックしないほど賢く、事実上、そうでない場合の99.9%です。面白い部分は、小さいサイズのコピー(キャッシュライン未満)が非常に支配的であるため、小さいサイズのものの「memcpy」が高速であることは本当に重要です。
bestsss 2011

16

Javaで書くことはできません。ネイティブコードは、オブジェクトの配列とプリミティブの配列の違いを無視または排除することができます。Javaは、少なくとも効率的にはそれを行うことができません。

また、配列のオーバーラップに必要なセマンティクスのため、単一ので記述することはできませんmemcpy()


5
いいでしょうmemmove。私はそれがこの質問の文脈で大きな違いを生むとは思わないが。
ペーテルTörök

memmove()でもありません。別の回答については、@ StephenCのコメントを参照してください。
ローンの侯爵2011年

それはたまたま私自身の答えだったので、すでにそれを見ました;-)しかしとにかく感謝します。
ペーテルTörök

1
重複する@Geek配列。ソース配列とターゲット配列が同じでオフセットのみが異なる場合、動作は慎重に指定され、memcpy()は準拠しません。
ローンの侯爵2013

1
それはできませんJavaで書かれたこと?Objectのサブクラスを処理するためのジェネリックメソッドを1つ作成し、次にプリミティブ型ごとに1つ作成することはできませんか?
Michael Dorst 2013年

10

もちろん、実装に依存します。

HotSpotはそれを「固有の」ものとして扱い、呼び出しサイトにコードを挿入します。それはマシンコードであり、遅い古いCコードではありません。これは、メソッドのシグネチャに関する問題が大幅に解消されることも意味します。

単純なコピーループは、明らかな最適化を適用できるほど単純です。たとえば、ループ展開。正確に何が起こるかは、やはり実装に依存します。


2
これは非常にまともな答えです:)、特に。組み込み関数について言及します。それらがない場合、通常はJITによって展開されるため、単純な反復の方が高速である可能性があります
2011

4

私自身のテストでは、複数の次元の配列をコピーするためのSystem.arraycopy()は、forループのインターリーブよりも10〜20倍高速です。

float[][] foo = mLoadMillionsOfPoints(); // result is a float[1200000][9]
float[][] fooCpy = new float[foo.length][foo[0].length];
long lTime = System.currentTimeMillis();
System.arraycopy(foo, 0, fooCpy, 0, foo.length);
System.out.println("native duration: " + (System.currentTimeMillis() - lTime) + " ms");
lTime = System.currentTimeMillis();

for (int i = 0; i < foo.length; i++)
{
    for (int j = 0; j < foo[0].length; j++)
    {
        fooCpy[i][j] = foo[i][j];
    }
}
System.out.println("System.arraycopy() duration: " + (System.currentTimeMillis() - lTime) + " ms");
for (int i = 0; i < foo.length; i++)
{
    for (int j = 0; j < foo[0].length; j++)
    {
        if (fooCpy[i][j] != foo[i][j])
        {
            System.err.println("ERROR at " + i + ", " + j);
        }
    }
}

これは印刷します:

System.arraycopy() duration: 1 ms
loop duration: 16 ms

9
この質問は古いものですが、記録のために:これは公正なベンチマークではありません(そのようなベンチマークがそもそも意味があるかどうかの質問は言うまでもありません)。System.arraycopy浅いコピーを実行します(内部のsへの参照のみfloat[]がコピーされます)が、ネストされたfor-loopsは(floatによってfloat)深いコピーを実行します。への変更fooCpy[i][j]は、のfoo使用System.arraycopyに反映されますが、ネストされたforループは使用されません。
misberner 2013年

4

いくつかの理由があります:

  1. JITは、手動で記述されたCコードほど効率的な低レベルコードを生成する可能性は低いです。低レベルのCを使用すると、一般的なJITコンパイラーでは不可能に近い多くの最適化が可能になります。

    手書きのC実装のいくつかのトリックと速度の比較については、このリンクを参照してください(memcpyですが、原則は同じです):この最適化Memcpyが速度を向上させることを確認してください

  2. Cバージョンは、配列メンバーのタイプとサイズにほとんど依存しません。配列の内容をメモリの生のブロック(ポインタなど)として取得する方法がないため、Javaで同じことを行うことはできません。


1
Javaコードは最適化できます。実際にはどのような実際に起こることは、マシンコードがC.よりも効率的ですが生成されている
トム・ホーティン- tackline

JITされたコードは、実行されているプロセッサを認識しているため、ローカルで最適化される場合があることに同意します。ただし、「ジャストインタイム」であるため、実行に時間がかかる非ローカル最適化をすべて使用することはできません。また、手作りのCコードと一致することは決してありません(特定のプロセッサ用にコンパイルするか、ある種のランタイムチェックによって、プロセッサを考慮に入れ、JITの利点を部分的に無効にする可能性もあります)。
HrvojePrgeša

1
Sun JITコンパイラチームは、これらの点の多くに異議を唱えると思います。たとえば、HotSpotは不要なメソッドディスパッチを削除するためにグローバル最適化を行うと私は信じています。JITがプロセッサ固有のコードを生成できない理由はありません。次に、JITコンパイラが現在のアプリケーション実行の実行動作に基づいてブランチの最適化を実行できるという点があります。
スティーブンC

@Stephen C-ブランチの最適化に関する優れた点ですが、C / C ++コンパイラで静的パフォーマンスプロファイリングを実行して、同様の効果を実現することもできます。また、ホットスポットには2つの動作モードがあると思います。デスクトップアプリケーションは利用可能な最適化のすべてを使用して妥当な起動時間を達成するわけではありませんが、サーバーアプリケーションはより積極的に最適化されます。全体として、いくつかの利点がありますが、いくつかの利点も失われます。
HrvojePrgeša

1
System.arrayCopyはCを使用して実装されていないため、この回答は無効になります
Nitsan Wakart 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.