質問
OpenGL ライブラリで使用する Matrix クラスを作成しているときに、データを格納するために Java 配列を使用するか、バッファ戦略を使用するかという問題に遭遇しました (JOGL は、Matrix 操作の直接バッファ コピーを提供します)。これを分析するために、私は小さなパフォーマンス テスト プログラムを作成しました。このプログラムは、配列、バッファー、ダイレクト バッファーでのループ操作とバルク操作の相対速度を比較します。
私の結果をここであなたと共有したいと思います (かなり興味深いと思います)。お気軽にコメントおよび/または間違いを指摘してください。コードは、 pastebin.com
/is7UaiMV
で表示できます。
ノート
ループ読み取り配列はA[i] = B[i]として実装されます。それ以外の場合、JIT オプティマイザーはそのコードを完全に削除します。実際のvar = A[i]はほとんど同じようです。
配列サイズ 10,000 のサンプル結果では、JIT オプティマイザーがループ配列アクセスを System.arraycopy のような実装に置き換えた可能性が非常に高いです。
Java はA.get(B)をB.put(A)として実装しているため、バルク取得バッファ -> バッファはありません。したがって、結果はバルクプットの結果と同じになります。
結論
ほとんどすべての状況で、Java 内部配列を使用することを強くお勧めします。put/get 速度が大幅に高速になるだけでなく、JIT は最終的なコードでより優れた最適化を実行できます。
バッファーは、次の両方が当てはまる場合にのみ使用する必要があります。
- 大量のデータを処理する必要があります。
- そのデータは、ほとんどまたは常に一括処理されます。
backened-buffer には、バッファの内容を裏付ける Java Array があることに注意してください。put/get をループする代わりに、このバックバッファで操作を行うことをお勧めします。
ダイレクト バッファは、メモリの使用量が気になり、基になるデータにアクセスしない場合にのみ使用してください。非直接バッファよりもわずかに遅く、基になるデータにアクセスする場合ははるかに遅くなりますが、使用するメモリは少なくなります。さらに、ダイレクト バッファを使用する場合、バイト以外のデータ (float 配列など) をバイトに変換すると、余分なオーバーヘッドが発生します。
詳細については、こちらを参照してください。
サンプル結果
注: パーセンテージは読みやすくするためだけのものであり、実際の意味はありません。
10,000,000回の反復でサイズ16の配列を使用...
-- Array tests: -----------------------------------------
Loop-write array: 87.29 ms 11,52%
Arrays.fill: 64.51 ms 8,51%
Loop-read array: 42.11 ms 5,56%
System.arraycopy: 47.25 ms 6,23%
-- Buffer tests: ----------------------------------------
Loop-put buffer: 603.71 ms 79,65%
Index-put buffer: 536.05 ms 70,72%
Bulk-put array->buffer: 105.43 ms 13,91%
Bulk-put buffer->buffer: 99.09 ms 13,07%
Bulk-put bufferD->buffer: 80.38 ms 10,60%
Loop-get buffer: 505.77 ms 66,73%
Index-get buffer: 562.84 ms 74,26%
Bulk-get buffer->array: 137.86 ms 18,19%
-- Direct buffer tests: ---------------------------------
Loop-put bufferD: 570.69 ms 75,29%
Index-put bufferD: 562.76 ms 74,25%
Bulk-put array->bufferD: 712.16 ms 93,96%
Bulk-put buffer->bufferD: 83.53 ms 11,02%
Bulk-put bufferD->bufferD: 118.00 ms 15,57%
Loop-get bufferD: 528.62 ms 69,74%
Index-get bufferD: 560.36 ms 73,93%
Bulk-get bufferD->array: 757.95 ms 100,00%
100,000回の反復でサイズ1,000の配列を使用...
-- Array tests: -----------------------------------------
Loop-write array: 22.10 ms 6,21%
Arrays.fill: 10.37 ms 2,91%
Loop-read array: 81.12 ms 22,79%
System.arraycopy: 10.59 ms 2,97%
-- Buffer tests: ----------------------------------------
Loop-put buffer: 355.98 ms 100,00%
Index-put buffer: 353.80 ms 99,39%
Bulk-put array->buffer: 16.33 ms 4,59%
Bulk-put buffer->buffer: 5.40 ms 1,52%
Bulk-put bufferD->buffer: 4.95 ms 1,39%
Loop-get buffer: 299.95 ms 84,26%
Index-get buffer: 343.05 ms 96,37%
Bulk-get buffer->array: 15.94 ms 4,48%
-- Direct buffer tests: ---------------------------------
Loop-put bufferD: 355.11 ms 99,75%
Index-put bufferD: 348.63 ms 97,93%
Bulk-put array->bufferD: 190.86 ms 53,61%
Bulk-put buffer->bufferD: 5.60 ms 1,57%
Bulk-put bufferD->bufferD: 7.73 ms 2,17%
Loop-get bufferD: 344.10 ms 96,66%
Index-get bufferD: 333.03 ms 93,55%
Bulk-get bufferD->array: 190.12 ms 53,41%
100,000回の反復でサイズ10,000の配列を使用...
-- Array tests: -----------------------------------------
Loop-write array: 156.02 ms 4,37%
Arrays.fill: 109.06 ms 3,06%
Loop-read array: 300.45 ms 8,42%
System.arraycopy: 147.36 ms 4,13%
-- Buffer tests: ----------------------------------------
Loop-put buffer: 3385.94 ms 94,89%
Index-put buffer: 3568.43 ms 100,00%
Bulk-put array->buffer: 159.40 ms 4,47%
Bulk-put buffer->buffer: 5.31 ms 0,15%
Bulk-put bufferD->buffer: 6.61 ms 0,19%
Loop-get buffer: 2907.21 ms 81,47%
Index-get buffer: 3413.56 ms 95,66%
Bulk-get buffer->array: 177.31 ms 4,97%
-- Direct buffer tests: ---------------------------------
Loop-put bufferD: 3319.25 ms 93,02%
Index-put bufferD: 3538.16 ms 99,15%
Bulk-put array->bufferD: 1849.45 ms 51,83%
Bulk-put buffer->bufferD: 5.60 ms 0,16%
Bulk-put bufferD->bufferD: 7.63 ms 0,21%
Loop-get bufferD: 3227.26 ms 90,44%
Index-get bufferD: 3413.94 ms 95,67%
Bulk-get bufferD->array: 1848.24 ms 51,79%