System.arraycopyがネイティブメソッドであることをJavaソースで確認しました。
もちろん、理由はそれが速いからです。しかし、コードが採用できるネイティブトリックはどのようなものですか?
元の配列をループして、各ポインターを新しい配列にコピーするだけではありません。確かに、これはそれほど遅くて面倒ではありませんか?
ネイティブコードでは、n個別のコピーではなく、単一のmemcpy
/ memmove
で実行できます操作。パフォーマンスの違いはかなりのものです。
Javaで書くことはできません。ネイティブコードは、Objectの配列とプリミティブの配列の違いを無視または排除できます。 Javaは、少なくとも効率的にはできません。
そして、それはできない単一のmemcpy()
で書くことができます。これは、配列の重複に必要なセマンティクスのためです。
もちろん、実装に依存します。
HotSpotはそれを「固有」として扱い、呼び出しサイトにコードを挿入します。それはマシンコードであり、古いCコードを遅くするものではありません。これはまた、メソッドのシグネチャに関する問題がほとんどなくなることを意味します。
単純なコピーループは、明らかな最適化を適用できるほど単純です。たとえば、ループの展開。正確に何が起こるかは、実装に依存します。
私自身のテストでは、複数の次元配列をコピーするSystem.arraycopy()は、ループのインターリーブよりも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
いくつかの理由があります。
JITは、手動で記述されたCコードほど効率的な低レベルコードを生成する可能性は低いです。低レベルCを使用すると、汎用JITコンパイラでは不可能に近い多くの最適化を有効にできます。
手書きC実装のいくつかのトリックと速度の比較については、このリンクを参照してください(memcpy、ただし原則は同じです):これを確認してください Memcpyを最適化すると速度が向上します
Cバージョンは、配列メンバーのタイプとサイズにほとんど依存しません。 Javaで同じことを行うことはできません。なぜなら、配列の内容を生のメモリブロック(ポインタなど)として取得する方法がないためです。